这似乎是最常讨论的主题之一,但我找不到真正有效的解决方案。我发布这个问题是为了分享我找到的解决方案以及希望找到更好/更清洁的解决方案
情况描述:
我的应用程序中有一个UIWebview
网页视图中有文字输入/区域
长按文本区/输入会显示一个上下文菜单,其中包括“剪切”,“复制”,“定义”等。等
我们需要在不禁用用户输入的情况下禁用此菜单。
到目前为止我尝试了什么 (那些不起作用的东西):
此解决方案告诉我们将canPerformAction:withSender:
添加到UIWebview的子类或UIWebview的委托中。
- (BOOL) canPerformAction:(SEL)action withSender:(id)sender
{
if (action == @selector(defineSelection:))
{
return NO;
}
else if (action == @selector(translateSelection:))
{
return NO;
}
else if (action == @selector(copy:))
{
return NO;
}
return [super canPerformAction:action withSender:sender];
}
不起作用,因为没有为显示的菜单项调用此类中的canPerformAction:
。
由于sharedMenuController与Responder链中的第一个响应者进行交互,因此在容器中实现canPerformAction会跳过select和selectAll,因为它们已经被子菜单处理过了。
将以下内容添加到CSS:
html {
-webkit-user-select: none;
-webkit-touch-callout: none;
-webkit-tap-highlight-color:rgba(0,0,0,0);
}
这适用于图像和超链接,但不适用于输入。 :(
答案 0 :(得分:7)
第一个解决方案无效的根本原因是名为UIWebBrowserView
的子视图。这似乎是canPerformAction为上下文菜单中显示的任何action
返回true的视图。
由于此UIWebBrowserView
是私有类,我们不应该尝试将其子类化(因为它会让您的应用被拒绝)。
所以我们做的是我们制作另一个名为mightPerformAction:withSender:
的方法,就像这样 -
- (BOOL)mightPerformAction:(SEL)action withSender:(id)sender {
NSLog(@"******Action!! %@******",NSStringFromSelector(action));
if (action == @selector(copy:))
{
NSLog(@"Copy Selector");
return NO;
}
else if (action == @selector(cut:))
{
NSLog(@"cut Selector");
return NO;
}
else if (action == NSSelectorFromString(@"_define:"))
{
NSLog(@"define Selector");
return NO;
}
else if (action == @selector(paste:))
{
NSLog(@"paste Selector");
return NO;
}
else
{
return [super canPerformAction:action withSender:sender];
}
}
并添加另一个方法来替换canPerformAction:withSender:with mightPerformAction:withSender:
- (void) replaceUIWebBrowserView: (UIView *)view
{
//Iterate through subviews recursively looking for UIWebBrowserView
for (UIView *sub in view.subviews) {
[self replaceUIWebBrowserView:sub];
if ([NSStringFromClass([sub class]) isEqualToString:@"UIWebBrowserView"]) {
Class class = sub.class;
SEL originalSelector = @selector(canPerformAction:withSender:);
SEL swizzledSelector = @selector(mightPerformAction:withSender:);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(self.class, swizzledSelector);
//add the method mightPerformAction:withSender: to UIWebBrowserView
BOOL didAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
//replace canPerformAction:withSender: with mightPerformAction:withSender:
if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
}
}
最后在ViewController的viewDidLoad中调用它:
[self replaceUIWebBrowserView:self.webView];
注意:将#import <objc/runtime.h>
添加到viewController,然后不会显示错误(方法)。
注意:我正在使用NSSelectorFromString
方法来避免在审核过程中检测到私有API选择器。
这似乎在iOS7中使用Xcode 5正常工作,如果有人能在此找到任何问题,请告诉我如何改进它..
答案 1 :(得分:2)
你也可以隐藏菜单:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(menuWillBeShown:) name:UIMenuControllerWillShowMenuNotification object:nil];
...
- (void)menuWillBeShown:(NSNotification *)notification {
dispatch_async(dispatch_get_main_queue(),^{
[[UIMenuController sharedMenuController] setMenuVisible:NO animated:NO];
});
}
这里的基本技巧是dispatch_async
。