web-dev-qa-db-fra.com

Ouvrir les liens target = "_ blank" en dehors de UIWebView dans Safari

Dans mon application iOS, j'ai un UIWebView. 

Maintenant, je veux que tous les liens ayant l'attribut target = "_ blank" ne s'ouvrent pas dans mon WebView mais en externe dans Safari. 

Comment puis-je faire ceci?

19
Norwald2

Ma réponse, qui provient d'une réponse trouvée sur le débordement de pile pour Android WebView. Mais en réalité, les deux webview ont le même problème et le même correctif (sale):

- (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];
}

Cela résout à la fois le problème target = "_ blank" à ouvrir dans le safari, ET continue d'ouvrir les liens standard dans la vue Web.

35
Benjamin Piette

Le problème avec la solution de wedo est que tous vos liens s'ouvriront dans Safari.

Deux solutions:

1 - Rappel JavaScript sur Objective-C lorsque target = "_ blank"
Pour résoudre votre problème, vous devez ajouter du javascript sur tous vos liens, vérifier s’ils ont l’attribut _blank, puis rappeler votre code Objective-C depuis JavaScript et exécuter:

[[UIApplication sharedApplication] openURL:myUrl];

Personnellement, je n'aime pas cette solution parce que c'est beaucoup de code, de callbacks, de complexité et un peu compliqué ...

2 - Vérification du paramètre de l'URL
Si vous avez accès au code HTML (vous devez avoir accès au code HTML dans les deux solutions). Je vous recommande de supprimer le target = "_ blank" et d'ajouter le paramètre? OpenInSafari = true

Dans le UIWebViewDelegate, ajoutez le code suivant:

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

Un bon (mauvais?) Point avec cette solution est que si le niveau de lien x plus profond peut toujours ouvrir des liens dans le navigateur Safari 

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


Toujours ajouter le protocole dans l'URL (http, https ...)

4
Martin Magakian

Bravo à Martin Magakian! Voici la modification basée sur la suggestion de 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

J'avais la même question et, malheureusement, ces réponses m'ont entraîné d'une manière complètement fausse et très complexe. La réponse à la question est aussi simple que "vous devez utiliser WebViewPolicyDelegateProtocol".

Dans -viewDidLoad de l'implémentation du contrôleur de vue que vous écrivez:

[myWebView setPolicyDelegate:self];

Dans votre interface de classe de contrôleur de vue, vous devez ajouter deux éléments:

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

Et implémentez-les aussi facilement que:

- (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];
    }
}

Reportez-vous également au docs Apple

J'ai utilisé cette méthode dans mon projet, où frameset est utilisé dans le code HTML racine, chargé dans WebView. Toutes les liaisons croisées pointant vers une autre trame existante ne provoquent pas le deuxième appel de message, seules les nouvelles cibles (externes) sont traitées ici. Cela fonctionne bien pour moi. 

0
DeadlineX

J'ai basé ma réponse sur celle de Benjamin Piette mais j'avais besoin d'ajuster le script car les liens à ajuster dans mon cas avaient été générés de manière asynchrone par un autre 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