it-roy-ru.com

Открыть цель = "_ пустые" ссылки вне UIWebView в Safari

Внутри моего iOS-приложения у меня есть UIWebView. 

Теперь я хочу, чтобы все ссылки с атрибутом target = "_ blank" открывались не внутри моего WebView, а снаружи в Safari. 

Как я могу это сделать?

19
Norwald2

Мой ответ, который я нашел в ответе на переполнение стека для Android 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];
}

Это решает проблему target = "_ blank", открывающуюся в сафари, и продолжает открывать стандартные ссылки в веб-представлении.

35
Benjamin Piette

Проблема с решением wedo заключается в том, что все ваши ссылки будут открываться в Safari.

Два решения:

1 - обратный вызов JavaScript в Objective-C, когда target = "_ blank"
Чтобы решить вашу проблему, вам нужно добавить некоторые javascript во все ваши ссылки, проверить, имеют ли они атрибут _blank, затем отозвать свой код Objective-C из JavaScript и запустить:

[[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;
}

Хороший (плохой?) Смысл этого решения заключается в том, что, если ссылка на более глубоких уровнях все еще может открывать ссылки в браузере Safari 

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


Всегда добавляйте протокол в URL (http, https ...)

4
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
mpemburn

У меня был тот же вопрос, и, к сожалению, эти ответы привлекли меня совершенно неправильно и очень сложно. На самом деле на этот вопрос отвечает так же просто, как «вам нужно использовать 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

Я использовал этот способ в своем проекте, где frameset используется в корневом HTML, загруженном в WebView. Все перекрестные ссылки, указывающие на другой существующий фрейм, не вызывают второй вызов сообщения, поэтому здесь обрабатываются только новые (внешние) цели. У меня все в порядке. 

0
DeadlineX

Я основал свой ответ на ответе Бенджамина Пиетта, но мне нужно было скорректировать сценарий, так как ссылки, которые должны быть скорректированы в моем случае, были сгенерированы асинхронно другим 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];
}
0
clauswey