【问题标题】:Open target="_blank" links outside of UIWebView in Safari在 Safari 中的 UIWebView 之外打开 target="_blank" 链接
【发布时间】:2012-01-19 09:00:02
【问题描述】:

在我的 iOS 应用程序中,我有一个 UIWebView。

现在我希望所有具有属性 target="_blank" 的链接不要在我的 WebView 内部打开,而是在 Safari 外部打开。

我该怎么做?

【问题讨论】:

    标签: ios ios4 uiwebview ios5


    【解决方案1】:

    我的答案来自我在 Android WebView 的堆栈溢出中找到的答案。但实际上,两个 webview 都有相同的问题和相同的(脏)修复:

    - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request     navigationType:(UIWebViewNavigationType)navigationType
    {
        if ([request.URL.absoluteString hasPrefix:@"newtab:"])
        {
            // JS-hacked URl is a target=_blank url - manually open the browser.
            NSURL *url = [NSURL URLWithString:[request.URL.absoluteString substringFromIndex:7]];
            [[UIApplication sharedApplication] openURL:url];
    
            return true;
        }
    
        return true;
    }
    
    - (void)webViewDidFinishLoad:(UIWebView *)webView
    {
        // JS Injection hack to solve the target="_blank" issue and open a real browser in such case.
        NSString *JSInjection = @"javascript: var allLinks = document.getElementsByTagName('a'); if (allLinks) {var i;for (i=0; i<allLinks.length; i++) {var link = allLinks[i];var target = link.getAttribute('target'); if (target && target == '_blank') {link.setAttribute('target','_self');link.href = 'newtab:'+link.href;}}}";
        [webView stringByEvaluatingJavaScriptFromString:JSInjection];
    }
    

    这解决了在 safari 中打开 target="_blank" 的问题,并在 webview 中保持打开标准链接。

    【讨论】:

    • 这是一个非常有趣的方法。我只是用它来更改第 3 方网站的内容,以便 UIWebViewDelegate 方法会被一致地调用。可能是 UIWebView 的一个缺点,它与各种目标值的链接对委托的响应和交互方式不同。
    • 这个答案的安卓应用版本的链接是什么?
    • 我不记得 android 版本在哪里,但类似的东西可能会有所帮助:stackoverflow.com/questions/8578332/…
    • Android 可以通过WebChromeClient.onCreateWindow 方法在技术上拦截&lt;a target="_blank" ... 链接甚至javascript window.open(....。虽然你有一个不同的问题,因为 URL 没有在方法中给出,并且必须做其他骇人听闻的事情。
    • 这实际上是我用于项目的解决方案,(尽管使用 jQuery)但是我使用了自定义方案 native:// 和命令,必须在 javascript 中转义 URL,然后取消转义它回到webView:shouldStartLoadWithRequest:...,但这个简单的前置要干净得多。另外,我需要在一个不能依赖 jQuery 的新项目上执行此操作,因此您的基本 javascript 非常有用。谢谢!
    【解决方案2】:

    wedo 解决方案的问题是所有链接都会在 Safari 中打开。

    两种解决方案:

    1 - 当 target="_blank" 时 JavaScript 回调到 Objective-C
    要解决您的问题,您需要在所有链接上添加一些 javascript,检查它们是否具有属性 _blank,然后从 JavaScript 回调您的 Objective-C 代码并运行:

    [[UIApplication sharedApplication] openURL:myUrl];
    

    我个人不喜欢这个解决方案,因为它有很多代码、回调、复杂并且有点棘手......

    2 - 检查 url 参数
    如果您可以访问 HTML 代码(请注意在这两种解决方案中您都需要访问 HTML)我建议您删除 target="_blank" 并添加参数 ?openInSafari=true

    在 UIWebViewDelegate 中添加如下代码:

    -(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
        if (navigationType == UIWebViewNavigationTypeLinkClicked) {
            NSURL *url = [request URL];
            NSDictionary *param = [url queryParameters];
            NSString *openIsSafari = [param objectForKey:@"openInSafari"];
    
            if ( openIsSafari!= nil && ([openIsSafari isEqualToString:@"true"] ||  [openIsSafari isEqualToString:@"1"])){
                [[UIApplication sharedApplication] openURL:url];
                return NO;
            }
        }
        return YES;
    }
    

    这个解决方案的一个好(坏?)点是,如果链接 x 级​​别更深,仍然可以打开到 safari 浏览器的链接

    <a href="http://www.google.com?openInSafari=true">Google in Safari</a>
    


    始终在 URL 中添加协议(http、https...)

    【讨论】:

    • 喜欢你的版本。但是queryParameters 不是 NSURL 的可用函数。您需要解析来自[url query]的参数
    • 谢谢。我也试过这个,它对我有用 6/10 次,就像上面的答案一样。不确定它在同一个 webview 中打开 4 次而不是打开 Safari。不知道去哪里检查
    • stackoverflow.com/questions/58612520/… 请帮助解决这个相关问题。
    【解决方案3】:

    向 Martin Magakian 致敬!以下是根据 spankmaster79 的建议进行的修改:

    - (BOOL) webView:(UIWebView *)webView shouldStartLoadWithRequest: (NSURLRequest *)request navigationType: (UIWebViewNavigationType)navigationType {
        if (navigationType == UIWebViewNavigationTypeLinkClicked) {
            NSURL *url = [request URL];
            NSString *param = [url query];
    
            if ([param rangeOfString: @"openInSafari=true"].location != NSNotFound){
                [[UIApplication sharedApplication] openURL: url];
                return NO;
            }
        }
        return YES;
    }
    

    【讨论】:

      【解决方案4】:

      以防万一有人在 Swift4 中寻找答案

      对于内部加载,请确保使用 .cancel 调用 decisionHandler() 闭包,以便加载停止,同时调用 UIApplication.shared.open() 以在外部浏览器中打开 URL。

      func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
          if let url = navigationAction.request.url {
              if url.host == "your url.com" {
                  UIApplication.shared.open(url)
                  decisionHandler(.cancel)
                  return
              }
          }
      
          decisionHandler(.allow)
      }
      

      【讨论】:

        【解决方案5】:

        试试这个。

        UIApplication *app = [UIApplication sharedApplication];
        NSURL         *url = navigationAction.request.URL;
        
        if (!navigationAction.targetFrame) {
            if ([app canOpenURL:url]) {
                [app openURL:url];
                decisionHandler(WKNavigationActionPolicyCancel);
                return;
            }
        }
        if ([url.scheme isEqualToString:@"mailto"])
        {
            if ([app canOpenURL:url])
            {
                [app openURL:url];
                decisionHandler(WKNavigationActionPolicyCancel);
                return;
            }
        }
        
        decisionHandler(WKNavigationActionPolicyAllow);
        

        【讨论】:

          【解决方案6】:

          我的回答基于 Benjamin Piette 的回答,但需要调整脚本,因为在我的案例中要调整的链接是由其他 javascript 异步生成的。

          NSString* const kOpenInNewTabPrefix = @"myOpenInNewTabPrefix:";//This NEEDS to end with ':'
          
          - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request     navigationType:(UIWebViewNavigationType)navigationType
          {
              if ([[request.URL.absoluteString lowercaseString] hasPrefix:[kOpenInNewTabPrefix lowercaseString]])
              {
                  // JS-hacked URl is a target=_blank url - manually open the browser.
                  NSURL *url = [NSURL URLWithString:[request.URL.absoluteString substringFromIndex:[kOpenInNewTabPrefix length]]];
                  [[UIApplication sharedApplication] openURL:url];
          
                  return YES;
              }
          
              return YES;
          }
          
          - (void)webViewDidFinishLoad:(UIWebView *)webView
          {
              //based on http://stackoverflow.com/questions/8490038/open-target-blank-links-outside-of-uiwebview-in-safari
              // JS Injection hack to solve the target="_blank" issue and open a real browser in such case.
              NSString *JSInjection = [NSString stringWithFormat:@"javascript: "
                                       "document.getElementsByTagName('body')[0].addEventListener('click', function(e){"
                                       "  var a = e.target;"
                                       "  if(a.nodeName != 'A'){"
                                       "      return;"
                                       "  }"
                                       "  var target = a.target;"
                                       "  var href = a.href;"
                                       "  var prefix = '%@';"
                                       "  if(href.substring(0, %lu) != '%@' && target == '_blank'){"
                                       "      a.href = prefix + href;"
                                       "  }"
                                       "})"
                                       , [kOpenInNewTabPrefix lowercaseString]
                                       , (unsigned long)[kOpenInNewTabPrefix length]
                                       , [kOpenInNewTabPrefix lowercaseString]];
              [webView stringByEvaluatingJavaScriptFromString:JSInjection];
          }
          

          【讨论】:

            【解决方案7】:

            我有同样的问题,不幸的是,这些答案以一种完全错误且非常复杂的方式吸引了我。确实,问题的答案很简单,就像“您需要使用 WebViewPolicyDelegateProtocol”。

            在您编写的视图控制器实现的 -viewDidLoad 中:

            [myWebView setPolicyDelegate:self];
            

            在您的视图控制器类接口中,您必须添加两项:

            - (void)webView:(WebView *)webView 
            decidePolicyForNavigationAction:(NSDictionary *)actionInformation 
                    request:(NSURLRequest *)request 
                      frame:(WebFrame *)frame 
            decisionListener:(id<WebPolicyDecisionListener>)listener;
            - (void)webView:(WebView *)webView 
            decidePolicyForNewWindowAction:(NSDictionary *)actionInformation 
                    request:(NSURLRequest *)request 
               newFrameName:(NSString *)frameName 
            decisionListener:(id<WebPolicyDecisionListener>)listener;
            

            并像这样简单地实现它们:

            - (void)webView:(WebView *)webView 
            decidePolicyForNavigationAction:(NSDictionary *)actionInformation 
                    request:(NSURLRequest *)request 
                      frame:(WebFrame *)frame 
            decisionListener:(id<WebPolicyDecisionListener>)listener {
                // just the default behavior, though you're free to add any url filtering you like...
                [listener use];
            }
            - (void)webView:(WebView *)webView 
            decidePolicyForNewWindowAction:(NSDictionary *)actionInformation 
                    request:(NSURLRequest *)request 
               newFrameName:(NSString *)frameName 
            decisionListener:(id<WebPolicyDecisionListener>)listener {
                // frameName is your "target" parameter value
                if([frameName isEqualToString:@"_blank"]) {
                  [[NSWorkSpace sharedWorkSpace] loadURL:[request URL]];
                } else {
                    [listener use];
                }
            }
            

            另请参阅Apple docs

            我在我的项目中使用了这种方式,其中框架集用于根 HTML 中,加载到 WebView 中。所有指向另一个现有框架的交叉链接都不会导致第二个消息调用,因此这里只处理新的(外部)目标。它对我来说没问题。

            【讨论】:

            • 问题是关于 UIWebView (iOS),而不是 macOS。
            • 询问 iOS 但您的回答是关于 mac OSX。
            猜你喜欢
            • 2012-07-06
            • 2015-08-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-12-02
            • 2023-03-10
            • 1970-01-01
            • 2023-03-21
            相关资源
            最近更新 更多