WKWebView自定义长按菜单工作,但有一些重大问题

当用户长按一个链接时,出现一个警报控制器,其中包含以下选项:

  • 打开
  • 在新标签中打开
  • 复制

目前有两个问题:

  1. 如果用户在WKWebView完成导航之前执行长按,则默认(Safari)警报控制器出现。

  2. 如果用户在popup的animation出现之后抬起手指,WKWebView将其注册为轻击并导航到该链接,同时警报控制器仍然显示在屏幕上。

这个机制有三个部分。

首先,

在WKWebView完成导航之后,JavaScript被注入到禁用默认警报控制器的页面。

- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { [_webView evaluateJavaScript:@"document.body.style.webkitTouchCallout='none';" completionHandler:^(id result, NSError *error){ NSLog(@"Javascript: {%@, %@}", result, error.description); }]; } 

其次,

UILongPressGestureRecognizer被添加到WKWebView中,并被实现,以便根据触摸的位置find元素的属性。

 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { return YES; } - (void)longPress:(UILongPressGestureRecognizer *)longPressGestureRecognizer { if (longPressGestureRecognizer.state == UIGestureRecognizerStateBegan) { _shouldCancelNavigation = YES; CGPoint touchLocation = [longPressGestureRecognizer locationInView:_webView]; NSString *javascript = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Javascript" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil]; [_webView evaluateJavaScript:javascript completionHandler:^(id result, NSError *error){ NSLog(@"Javascript: {%@, %@}", result, error.description); }]; [_webView evaluateJavaScript:[NSString stringWithFormat:@"MyAppGetHTMLElementsAtPoint(%f,%f);", touchLocation.x, touchLocation.y] completionHandler:^(id result, NSError *error){ NSLog(@"Javascript: {%@, %@}", result, error.description); NSString *tags = (NSString *)result; if ([tags containsString:@",A,"]) { [_webView evaluateJavaScript:[NSString stringWithFormat:@"MyAppGetHREFAttributeAtPoint(%f,%f);", touchLocation.x, touchLocation.y] completionHandler:^(id result, NSError *error){ NSLog(@"Javascript: {%@, %@}", result, error.description); NSString *urlString = (NSString *)result; [_delegate webView:self didLongPressAtTouchLocation:touchLocation URL:[NSURL URLWithString:urlString]]; }]; return; } if ([tags containsString:@",IMG,"]) { [_webView evaluateJavaScript:[NSString stringWithFormat:@"MyAppGetSRCAttributeAtPoint(%f,%f);", touchLocation.x, touchLocation.y] completionHandler:^(id result, NSError *error){ NSLog(@"Javascript: {%@, %@}", result, error.description); NSString *urlString = (NSString *)result; [_delegate webView:self didLongPressAtTouchLocation:touchLocation imageWithSourceURL:[NSURL URLWithString:urlString]]; }]; return; } }]; } } 

最后,

提供警报控制器的委托方法在主ViewController上实现。

我对第二个问题的解决方法是添加一个布尔值shouldCancelNavigation,当警报控制器已经出现时是YES,当它已经被解散时是NO。

 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { if (_shouldCancelNavigation) { decisionHandler(WKNavigationActionPolicyCancel); } else { decisionHandler(WKNavigationActionPolicyAllow); } } 

有趣的是,网上有很多例子,链接不需要做出政策决定。 他们只是无法阻止他们而发生。

例如: http : //www.dribbble.com

在这里输入图像说明

资料来源: http : //www.icab.de/blog/2010/07/11/customize-the-contextual-menu-of-uiwebview/comment-page-3/

来源2: https : //github.com/mozilla-mobile/firefox-ios/pull/61

编辑

这解决了第二个问题,但我不确定它不会把某个地方弄坏。

 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { if ([otherGestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]) { otherGestureRecognizer.enabled = NO; otherGestureRecognizer.enabled = YES; } return YES; } 

编辑2

它确实造成了一个问题…因为上面的代码重置了内部长按手势识别器,所以不能再select文本。

编辑3

如果我完全删除我的实现(全部3步),并让每次长按链接的默认警报控制器踢第二个问题得到解决。

有一些关于苹果的警报控制器,防止WKWebView导航后,你举手指。

如果我没有错,在第二部分:

 - (void)longPress:(UILongPressGestureRecognizer *)longPressGestureRecognizer { if (longPressGestureRecognizer.state == UIGestureRecognizerStateBegan) { //Rest of your code ... } } 

您正在注入JavaScript来禁用系统对话框。 现在新闻结束后,WKWebview已经收到networking链接触发的事件。 由于为时已晚,为什么不尝试检查longPressGestureRecognizer.state等于UIGestureRecognizerStateEnded条件。

因此它变成了下面的代码。

  if (longPressGestureRecognizer.state == UIGestureRecognizerStateEnded) { //Rest of your code ... } 

我还没有testing过这个代码。 会更快乐,如果它的工作。

这个答案将解决第二个问题,但我不确定是否安全的App Store。

首先,您需要打破内部长按手势识别器,以便在用户抬起手指时不会触发。

 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { if ([otherGestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]) { if (otherGestureRecognizer.state == UIGestureRecognizerStateBegan) { // Warning: This will break how WKWebView handles selection of text. [otherGestureRecognizer requireGestureRecognizerToFail:gestureRecognizer]; } } return YES; } 

用户完成与自定义长按菜单的交互后,此代码将修复损坏的WKWebView:

  [_webView removeGestureRecognizer:_longPressGestureRecognizer]; // This code will remove the dependency and recover the lost functionality. _longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)]; _longPressGestureRecognizer.numberOfTouchesRequired = 1; _longPressGestureRecognizer.delegate = self; [_webView addGestureRecognizer:_longPressGestureRecognizer]; 

这是一个HACK