WKWebView自定义长按菜单工作,但有一些重大问题
当用户长按一个链接时,出现一个警报控制器,其中包含以下选项:
- 打开
- 在新标签中打开
- 复制
目前有两个问题:
-
如果用户在WKWebView完成导航之前执行长按,则默认(Safari)警报控制器出现。
-
如果用户在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 。