您如何真正从 UIMenuController 中删除 Copy

Posted

技术标签:

【中文标题】您如何真正从 UIMenuController 中删除 Copy【英文标题】:How do you REALLY remove Copy from UIMenuController 【发布时间】:2011-07-07 17:14:03 【问题描述】:

当您添加多个自定义菜单项时,显然曾经有一个easy way 来防止“更多...”标签出现在 UIMenuController 中。您只需删除所有系统菜单项。甚至还有一个 workaround here 仍然有复制工作。您只需使用不同的选择器实现自定义复制命令,然后覆盖 canPerformAction:withSender: 以不显示系统副本:

-(BOOL)canPerformAction:(SEL)action withSender:(id)sender 

    if (action == @selector(copy:))
       return NO;
    else
       // logic to show or hide other things

不幸的是,这种方法不再有效(至少在 UIWebView 子类中)。 canPerformAction:withSender: 为除了 copy: 之外的每个系统菜单项调用,因此结果是始终显示系统复制菜单项。这意味着如果您有多个自定义菜单项,它们总是隐藏在“更多...”后面

那么,有没有办法真正删除系统的复制项或其他方法来防止菜单项隐藏在“更多...”后面?

更新

这是我在重写 canPerformAction:withSender: 时得到的输出:请注意,“复制:”操作永远不会调用该方法:

cannot perform action cut: with sender <UIMenuController: 0x7227d30>.
cannot perform action select: with sender <UIMenuController: 0x7227d30>.
cannot perform action selectAll: with sender <UIMenuController: 0x7227d30>.
cannot perform action paste: with sender <UIMenuController: 0x7227d30>.
cannot perform action delete: with sender <UIMenuController: 0x7227d30>.
cannot perform action promptForReplace: with sender <UIMenuController: 0x7227d30>.
cannot perform action _showMoreItems: with sender <UIMenuController: 0x7227d30>.
cannot perform action _setRtoLTextDirection: with sender <UIMenuController: 0x7227d30>.
cannot perform action _setLtoRTextDirection: with sender <UIMenuController: 0x7227d30>.
can perform action customCopy: with sender <UIMenuController: 0x7227d30>.
can perform action custom1: with sender <UIMenuController: 0x7227d30>.
cannot perform action custom2: with sender <UIMenuController: 0x7227d30>.
can perform action custom3: with sender <UIMenuController: 0x7227d30>.
can perform action custom4: with sender <UIMenuController: 0x7227d30>.
cannot perform action cut: with sender <UIMenuController: 0x7227d30>.
cannot perform action select: with sender <UIMenuController: 0x7227d30>.
cannot perform action selectAll: with sender <UIMenuController: 0x7227d30>.
cannot perform action paste: with sender <UIMenuController: 0x7227d30>.
cannot perform action delete: with sender <UIMenuController: 0x7227d30>.
cannot perform action promptForReplace: with sender <UIMenuController: 0x7227d30>.
cannot perform action _showMoreItems: with sender <UIMenuController: 0x7227d30>.
cannot perform action _setRtoLTextDirection: with sender <UIMenuController: 0x7227d30>.
cannot perform action _setLtoRTextDirection: with sender <UIMenuController: 0x7227d30>.

【问题讨论】:

显然没有办法从 Objective-C 中的 UIMenuController 中删除副本,但可以使用 CSS:-webkit-user-select:none;iphonedevsdk.com/forum/iphone-sdk-development/… 也许我在开头的段落不够清楚,但我并没有试图阻止用户选择。我试图在 UIMenuController 中放置多个自定义菜单项,而不会卡在“更多...”菜单下。您以前可以通过阻止显示复制命令 (***.com/questions/4311009/…) 来做到这一点,但这不再有效。 “不再有效”是什么意思?它是在 ios5 中坏了还是什么? “不再有效”是指此处发布的解决方案:***.com/questions/3255070/… 不起作用。它显然曾经。其他人 (***.com/questions/4311009/…) 也发现此解决方案不起作用。正如我在更新中提到的,“复制:”永远不会调用被覆盖的方法。 @Ifalin 你终于找到新版本的解决方案了吗? 【参考方案1】:

您链接到的技术似乎仍然有效。我用这些方法实现了一个UIWebView子类,只出现了A项和B项。

+ (void)initialize

    UIMenuItem *itemA = [[UIMenuItem alloc] initWithTitle:@"A" action:@selector(a:)];
    UIMenuItem *itemB = [[UIMenuItem alloc] initWithTitle:@"B" action:@selector(b:)];
    [[UIMenuController sharedMenuController] setMenuItems:[NSArray arrayWithObjects:itemA, itemB, nil]];
    [itemA release];
    [itemB release];


- (BOOL)canPerformAction:(SEL)action withSender:(id)sender

    BOOL can = [super canPerformAction:action withSender:sender];
    if (action == @selector(a:) || action == @selector(b:))
    
        can = YES;
    
    if (action == @selector(copy:))
    
        can = NO;
    
    NSLog(@"%@ perform action %@ with sender %@.", can ? @"can" : @"cannot", NSStringFromSelector(action), sender);
    return can;

【讨论】:

我仍在使用这种技术,并且可以确认它在 iOS 4.3.2 中有效。不过,我还没有尝试过 iOS5 测试版。 不,它在 iOS5 中适用于默认选择器,如剪切、复制、粘贴、选择、全选、定义、建议。即使我返回 NO,这些仍然会显示。这既不适用于 UITextField 也不适用于 UIWebView。 我可以确认它可以在我的应用程序中运行,该应用程序是使用在 iOS4 和 iOS5 上运行的最新 iOS5 SDK 构建的。我隐藏了所有默认的复制/粘贴菜单项并显示我自己的 - 全部在自定义 UIWebView 子类上。 也确认了。它适用于 XCode 4.2 和 iOS 5.0.1,前提是您实现了下面定义的自定义 UIWebView 子类。 我将此答案标记为正确,因为它似乎对很多人都有效,但我想知道为什么它对我和其他人不起作用。【参考方案2】:

对于 ios >= 5.1 canPerformAction:(SEL)action withSender:(id)sender 不再工作。

如果您可以禁用粘贴操作,这是一种方法:

将 UITextFieldDelegate 添加到您的视图控制器并实现这样的方法

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
if(textField == txtEmailRe)
    return ((string.length) > 1 ? NO : YES);

这意味着如果用户为每个操作输入多个字符(这意味着用户可能正在粘贴某些内容。)不要在文本字段中接受它。

强制用户输入文本字段(如电子邮件和

)是一种很好的做法

【讨论】:

最好的解决方案,放下手! canPerformAction 在 IOS > 5.1 中确实有效。问题是您处理委托类中的 canPerformAction 。 canPerformAction 是在 UITextField 本身上调用的,而不是在委托上。为了使 canPerformAction 起作用,您应该在 UITextField 子类中覆盖 canPerformAction。【参考方案3】:

lemnar 的回答是正确的。实现 UIWebView 的子类就可以了。这个example 可以用于 UITextView。对于 UIWebView,创建自定义子类如下:

//
//  MyUIWebView.h
//

#import <UIKit/UIKit.h>

@interface MyUIWebView : UIWebView

@end

还有:

//
//  MyUIWebView.m
//

#import "MyUIWebView.h"

@implementation MyUIWebView

-(BOOL)canPerformAction:(SEL)action withSender:(id)sender 

    if (action == @selector(copy:))
        return NO;
    else
        // logic to show or hide other things


@end

然后,不要实例化 UIWebView,而是使用 MyUIWebView。

更新

如果想禁用“复制”但保留“定义”(和“翻译”),这可能很有用,这就是如何做到的;用这个替换上面的canPerformAction:withSender

-(BOOL)canPerformAction:(SEL)action withSender:(id)sender 

    if (action == @selector(defineSelection:))
    
        return YES;
    
    else if (action == @selector(translateSelection:))
    
        return YES; 
    
    else if (action == @selector(copy:))
    
        return NO;
    

    return [super canPerformAction:action withSender:sender];

【讨论】:

不,这在 iOS 5.0.1 中工作。刚试了下,canPerformAction:方法中还是没有copy:选择器。 这不应该在菜单中启用“复制”,而是禁用它。它在 iOS 5.0.1 上运行良好,前提是您按照上面给出的说明进行操作。 我写了“canPerformAction: 方法中仍然没有'copy:-selector'”。这意味着,复制操作永远不会发送到此委托方法,因此 canPerformAction 方法将永远无法返回 NO。这意味着,无法禁用复制。再说一遍:它不适用于 iOS 5.x。我在多台机器上对其进行了多次测试。它对你有用的唯一方法是,当你有一个旧的 Xcode/SDK 版本时,因为它以前可以工作。编辑:或者你可能没有使用 UIWebView。这个问题是关于 UIWebview 的。下面概述了唯一可行的解​​决方案(UIWebBrowserView 类别) auco,我不知道你为什么不能让它工作。正如上面@TomSwift 和我自己所说的(参见上面的自定义子类 MyUIWebView 的帖子),它在带有 XCode 4.2 的 iOS 5.0.1 上完美运行。有问题的类是 UIWebView,见上面的例子。我想在这里添加一个屏幕截图,但它会污染帖子并添加不必要的措辞。【参考方案4】:

这是适用于我的 iOS5.x 解决方案。 Josh Garnham 建议创建一个 UIWebBrowserView 类别来捕获复制:、粘贴:、定义:选择器。

http://ios-blog.co.uk/iphone-development-tutorials/rich-text-editing-highlighting-and-uimenucontroller-part-3/

@implementation UIWebBrowserView (UIWebBrowserView_Additions)
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender 
    return NO;

@end

请注意 FTR:在那个出色的网页上有一个轻微的错字。这正是你如何做到的。 Apple 将100% 拒绝。 建立一个类别

(您必须输入“UIWebBrowserView”,因为 Xcode 不会显示私有类。).h 和 .m 文件的全文:

// .h file...
#import "UIWebBrowserView+Tricky.h"
@interface UIWebBrowserView : UIView
@end
@interface UIWebBrowserView(Tricky)
@end

// .m file...
#import "UIWebBrowserView+Tricky.h"
@implementation UIWebBrowserView (Tricky)
-(BOOL)canPerformAction:(SEL)action withSender:(id)sender

NSLog(@"don't let Apple see this");
return NO;

@end

郑重声明,“单击”仍会显示烦人的拼写检查建议!但除此之外它确实完全删除了双击上下文菜单,它被 Apple 100% 拒绝。

【讨论】:

不幸的是,这似乎是一个私有方法,没有通过验证 通过一些表达,无需实际执行任何 AppStore 禁止的操作即可访问 UIWebBrowserView。 Objective C 是一种非常动态的语言。提示:动态子类化和 isa swizzling。 @JoeBlow 你不应该有!【参考方案5】:

对不起我的英语。 但是有一个想法。

我认为方法 canPerformAction 被调用了很多次,但你只处理一次。 在这种情况下,我认为可能有另一个 UI 控件调用了它。比如你的 UIWebView 中的 UITextView 控件。

我猜你可能会通过故事板生成 UI。并非故事板中的每个控件都有自己的类。您可以为响应控件定义一个类并重写其 canPerformAction 方法。

【讨论】:

你是对的,canPerformAction 是由 UIWebBrowserView 调用的,它是 UIWebView 的一个子视图,它返回 YES 进行复制。因此它没有调用我们编写的 canPerformAction。【参考方案6】:

您可以绘制自己的菜单,而不是使用 UIMenuController。这样一来,您可以在不使用其他的情况下同时显示任意数量的项目。

【讨论】:

以上是关于您如何真正从 UIMenuController 中删除 Copy的主要内容,如果未能解决你的问题,请参考以下文章

如何从 iOS 的 UIMenuController 中删除默认的 UIMenuItem?

如何从 UITextfields UIMenuController 中删除不需要的 UIMenuItems?

iOS:如何从 UIMenuController 获取选定的 UIMenuItem

如何从 UIMenuController 中删除 Copy、Select All、Define menuItem

捕捉复制/粘贴事件 UIMenuController

自定义 UIMenuController