在Safari中的UIWebView外部打开target =“_ blank”链接

时间:2011-12-13 13:27:48

标签: ios ios4 uiwebview ios5

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

现在我希望所有具有属性target =“_ blank”的链接不在我的WebView中打开,而是在Safari中外部打开。

我该怎么做?

7 个答案:

答案 0 :(得分:36)

我的回答,这是我在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中保持打开标准链接。

答案 1 :(得分:4)

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

两种解决方案:

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

[[UIApplication sharedApplication] openURL:myUrl];

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

2 - 检查网址参数
如果您有权访问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>


始终在网址(http,https ...)

中添加协议

答案 2 :(得分: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;
}

答案 3 :(得分:2)

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

答案 4 :(得分:1)

尝试一下。

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

答案 5 :(得分:0)

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

答案 6 :(得分:0)

我有同样的问题,不幸的是这些答案让我感到非常错误和非常复杂。实际上这个问题的回答简直就是&#34;你需要使用WebViewPolicyDelegateProtocol&#34;。

在您编写的视图控制器实现的-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

我在我的项目中使用过这种方式,其中frameset在根HTML中使用,加载到WebView中。指向另一个现有帧的所有交叉链接都不会导致第二个消息调用,因此此处仅处理新的(外部)目标。它适用于我。