【问题标题】:Disable long press menu in text area/input UIWebview在文本区域/输入 UIWebview 中禁用长按菜单
【发布时间】:2014-10-05 11:40:12
【问题描述】:

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

情况描述:

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

  • webview中有文本输入/区域

  • 长按文本区域/输入会弹出一个上下文菜单,其中包含“剪切”、“复制”、“定义”等。

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


到目前为止我尝试过的 (不起作用的东西):

覆盖 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);
}

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

【问题讨论】:

    标签: objective-c cordova uiwebview contextmenu


    【解决方案1】:

    第一个解决方案不起作用的根本原因是名为UIWebBrowserView 的子视图。对于上下文菜单中显示的任何action,这似乎是其 canPerformAction 返回 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: 替换为 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选择器。

    【讨论】:

    • 我今天提交了,还在审核中
    • 如果通过审核,我一定会更新答案。另外,我没有直接使用私有 api 选择器,而是使用 NSSelectorFromString。希望这足以绕过检测。其他一切看起来都很好。
    • 刚刚发现method_exchangeImplementations,也可能是个问题! :(
    • 该应用已获得 Apple 批准。欢呼!! =) 抱歉耽搁了@laucel
    • @rubanbs 我认为当检测到长按时,每个可能的菜单项都会调用canPerformAction:withSender:(或我们替换它的方法)。它为显示的项目返回YES。因此,您可以检查哪个action 返回YES。如果所有菜单项都返回“YES”,这意味着应该为此 UIElement 显示所有菜单项 PS:我已经有一段时间没有处理这个问题了,所以我可能错了。请仔细检查!
    【解决方案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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-04-14
      • 1970-01-01
      • 1970-01-01
      • 2011-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-12
      相关资源
      最近更新 更多