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

Vul*_*kan 10 javascript objective-c ios wkwebview

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

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

目前有两个问题:

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

  2. 如果用户在弹出动画发生后抬起手指,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);
               }];
}
Run Code Online (Sandbox Code Playgroud)

其次,

UILongPressGestureRecognizer被添加到WKWebView并实现,以便根据触摸位置查找元素的属性.

- (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;
                       }
                   }];
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,

呈现警报控制器的委托方法在主ViewController上实现.

我对第二个问题的解决方案是添加一个布尔值shouldCancelNavigation,当提示警报控制器时为YES,当它被解除时为NO.

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
    if (_shouldCancelNavigation) {

        decisionHandler(WKNavigationActionPolicyCancel);
    }
    else {

        decisionHandler(WKNavigationActionPolicyAllow);
    }
}
Run Code Online (Sandbox Code Playgroud)

有趣的是,网上有很多例子,其中链接不需要政策决定.他们只是发生了我无法阻止他们.

示例: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;
}
Run Code Online (Sandbox Code Playgroud)

编辑2:

它实际上确实产生了问题......由于上面的代码重置了内部长按手势识别器,因此您无法再选择文本.

编辑3:

如果我完全删除我的实现(所有3个步骤)并且每次我长按链接时让默认警报控制器启动,第二个问题就会解决.

苹果的警报控制器有一些东西阻止WKWebView在你举起手指后导航.

byJ*_*van 1

如果我没记错的话,在第二部分:

- (void)longPress:(UILongPressGestureRecognizer *)longPressGestureRecognizer
{
    if (longPressGestureRecognizer.state == UIGestureRecognizerStateBegan) {

//Rest of your code ...
    }
}
Run Code Online (Sandbox Code Playgroud)

您正在注入 javascript 来禁用系统对话框。现在新闻结束后,WKWebview 已经收到了网页链接触发的事件。既然已经太晚了,为什么不尝试检查条件longPressGestureRecognizer.state是否等于呢 UIGestureRecognizerStateEnded

因此它更改为以下代码。

    if (longPressGestureRecognizer.state == UIGestureRecognizerStateEnded) {

       //Rest of your code ...
    }
Run Code Online (Sandbox Code Playgroud)

我还没有测试过这段代码。如果有效的话就更开心了