簡體   English   中英

在 Safari 中打開 UIWebView 之外的 target="_blank" 鏈接

[英]Open target=“_blank” links outside of UIWebView in Safari

在我的 iOS 應用程序中,我有一個 UIWebView。

現在我希望所有具有屬性 target="_blank" 的鏈接不要在我的 WebView 內部打開,而是在 Safari 外部打開。

我怎樣才能做到這一點?

我的答案來自我在 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 中打開標准鏈接的問題。

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...)

向馬丁·馬加基安致敬! 這是基於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;
}

以防萬一有人在 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)
}

試試這個。

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);

我的答案基於 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];
}

我有同樣的問題,不幸的是,這些答案以一種完全錯誤且非常復雜的方式吸引了我。 實際上,這個問題的回答就像“您需要使用 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 文檔

我在我的項目中使用了這種方式,其中框架集在根 HTML 中使用,加載到 WebView 中。 指向另一個現有框架的所有交叉鏈接不會導致第二個消息調用,因此這里只處理新的(外部)目標。 它對我有用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM