Как добавить опцию меню во всплывающее меню NSTextAttachment UITextView?
Я хочу добавить еще один пункт меню к параметрам меню вложения изображений по умолчанию ("Копировать изображение", "Сохранить в камеру"). Обратите внимание, что эти параметры отображаются при длительном нажатии на изображение, встроенное в UITextView, если textView не находится в режиме редактирования.
Я попытался добавить пользовательское меню в uimenucontroller и использовать -(void)canPerformAction, чтобы включить или отключить эту опцию, однако это, кажется, добавляет пункт меню в меню редактирования uitextView и не влияет на всплывающее меню вложений.
-(void)canPerformAction никогда не вызывается при длительном нажатии на вложение изображения.
2 ответа
По словам Apple, для этого нет общедоступного API, однако оказалось, что довольно просто заменить стандартное меню на то, которое выглядит и ведет себя одинаково.
В viewController, который содержит UITextView, добавьте следующее или подобное и установите его в качестве делегата textView.
- (BOOL)textView:(UITextView *)textView shouldInteractWithTextAttachment:(NSTextAttachment *)textAttachment inRange:(NSRange)characterRange {
// save in ivar so we can access once action sheet option is selected
_attachment = textAttachment;
[self attachmentActionSheet:(UITextView *)textView range:characterRange];
return NO;
}
- (void)attachmentActionSheet:(UITextView *)textView range:(NSRange)range {
// get the rect for the selected attachment (if its a big image with top not visible the action sheet
// will be positioned above the top limit of the UITextView
// Need to add code to adjust for this.
CGRect attachmentRect = [self frameOfTextRange:range inTextView:textView];
_attachmentMenuSheet = [[UIActionSheet alloc] initWithTitle:nil
delegate:self
cancelButtonTitle:@"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:@"Copy Image", @"Save to Camera Roll", @"Open in Viewer", nil];
// Show the sheet
[_attachmentMenuSheet showFromRect:attachmentRect inView:textView animated:YES];
}
- (CGRect)frameOfTextRange:(NSRange)range inTextView:(UITextView *)textView {
CGRect rect = [textView.layoutManager boundingRectForGlyphRange:range inTextContainer:textView.textContainer];
// Now convert to textView coordinates
CGRect rectRange = [textView convertRect:rect fromView:textView.textInputView];
// Now convert to contentView coordinates
CGRect rectRangeSuper = [self.contentView convertRect:rectRange fromView:textView];
// Get the textView frame
CGRect rectView = textView.frame;
// Find the intersection of the two (in the same coordinate space)
CGRect rectIntersect = CGRectIntersection(rectRangeSuper, rectView);
// If no intersection then that's weird !!
if (CGRectIsNull(rectIntersect)) {
return rectRange;
}
// Now convert the intersection rect back to textView coordinates
CGRect rectRangeInt = [textView convertRect:rectIntersect fromView:self.contentView];
return rectRangeInt;
}
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex {
if (actionSheet == _attachmentMenuSheet) {
switch (buttonIndex) {
case 0:
[self copyImageToPasteBoard:[_attachment image]];
break;
case 1:
[self saveToCameraRoll:[_attachment image]];
break;
case 2:
[self browseImage:[_attachment image]];
break;
default:
break;
}
}
}
- (void)saveToCameraRoll:(UIImage*)image {
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
}
- (void)copyImageToPasteBoard:(UIImage*)image {
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
NSData *data = UIImagePNGRepresentation(image);
[pasteboard setData:data forPasteboardType:@"public.png"];
}
-(void)browseImage:(UIImage*)image
{
OSImageViewController *_imageViewerController = [[OSImageViewController alloc] init];
UIImage *img = [[UIImage alloc] initWithData:UIImagePNGRepresentation(image)];
_imageViewerController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
_imageViewerController.modalPresentationStyle = UIModalPresentationFullScreen;
_imageViewerController.delegate = self;
[self presentViewController:_imageViewerController animated:YES completion:^(void){
[_imageViewerController setImage:img];
}];
}
В iOS 17 добавлены новые методыUITextViewDelegate
чтобы настроить основное действие и контекстное меню длительного нажатия для изображений и ссылок.
optional func textView(
_ textView: UITextView,
primaryActionFor textItem: UITextItem,
defaultAction: UIAction
) -> UIAction?
optional func textView(
_ textView: UITextView,
menuConfigurationFor textItem: UITextItem,
defaultMenu: UIMenu
) -> UITextItem.MenuConfiguration?
https://developer.apple.com/documentation/uikit/uitextviewdelegate/4173165-textview
https://developer.apple.com/documentation/uikit/uitextviewdelegate/4173164-textview