如何将菜单选项添加到 NSTextAttachment 弹出菜单是 UITextView?

Posted

技术标签:

【中文标题】如何将菜单选项添加到 NSTextAttachment 弹出菜单是 UITextView?【英文标题】:How can I add a menu option to the NSTextAttachment popup menu is UITextView? 【发布时间】:2013-11-08 21:23:46 【问题描述】:

我想在默认图像附件菜单选项(复制图像、保存到相机胶卷)中添加另一个菜单选项。请注意,如果 textView 未处于编辑模式,则当您长按嵌入在 UITextView 中的图像时会显示这些选项。

我尝试向 uimenucontroller 添加自定义菜单并使用 -(void)canPerformAction 启用或禁用该选项,但这似乎将菜单项添加到 uitextView 的编辑菜单中,并且对附件弹出菜单没有影响。

-(void)canPerformAction 似乎永远不会在长按图像附件时被调用。

【问题讨论】:

【参考方案1】:

根据 Apple 的说法,没有用于执行此操作的公共 API,但事实证明,将默认菜单替换为外观和行为相同的菜单相对简单。

在包含 UITextView 的 viewController 中添加以下或类似内容并将其设置为 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];

    ];


【讨论】:

邓肯,这里的 self.contentView 是什么? 你应该可以使用 UITextView 的超级视图。在我的情况下,我有一个可滚动的表单,所以我有一个具有固定高度/可变宽度(以适应设备宽度)视图的滚动视图,其中放置了控件,包括 UITextView。我称之为 contentView。 那么调用 'textView.superview' 会更好,或者更一般地说? 是的,应该可以,但是根据您的视图层次结构,您可能需要做一些不同的事情。在这里查看更多细节,虽然我不记得我是否解释了为什么你需要找到事物的交集,因为如果它们已经被滚动,部分被遮挡的视图。 ossh.com.au/design-and-technology/software-development/… 可以这么说,如果 textView 已滚动以致图像被部分遮挡,您不希望弹出源位于任何其他剪切视图的 textView 的顶部边框之上(如果整个 contentView 已向上滚动——但我不确定我是否在代码示例中满足这种情况)。

以上是关于如何将菜单选项添加到 NSTextAttachment 弹出菜单是 UITextView?的主要内容,如果未能解决你的问题,请参考以下文章

如何将选项检查菜单添加到 CMFCToolBar 以允许同时进行多项检查

如何将自定义选项添加到 Microsoft Edge 右键菜单?

如何使用 C 或 C++ 将选项添加到 Windows 资源管理器上下文菜单?

Angular 5 Material 如何将下拉菜单添加到多级轮播样式的 SideNav?

将功能添加到窗口顶部的主菜单

将选项卡添加到现有选项卡菜单