禁用文本区域/输入UIWebview中的长按菜单

时间:2014-08-12 11:56:44

标签: objective-c cordova uiwebview contextmenu

这似乎是最常讨论的主题之一,但我找不到真正有效的解决方案。我发布这个问题是为了分享我找到的解决方案以及希望找到更好/更清洁的解决方案

情况描述:

  • 我的应用程序中有一个UIWebview

  • 网页视图中有文字输入/区域

  • 长按文本区/输入会显示一个上下文菜单,其中包括“剪切”,“复制”,“定义”等。等

我们需要在不禁用用户输入的情况下禁用此菜单。


到目前为止我尝试了什么 (那些不起作用的东西):

覆盖 canPerformAction

此解决方案告诉我们将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

将以下内容添加到CSS:

html {
    -webkit-user-select: none;
    -webkit-touch-callout: none;
    -webkit-tap-highlight-color:rgba(0,0,0,0);
}

这适用于图像和超链接,但不适用于输入。 :(

2 个答案:

答案 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