Liens ouverts UIWebView dans Safari

304

J'ai une UIWebView très simple avec le contenu de mon bundle d'application. Je souhaite que tous les liens de la vue Web s'ouvrent dans Safari plutôt que dans la vue Web. Est-ce possible?

David Beck
la source
La réponse acceptée ci-dessous ne fonctionnera pas dans tous les cas.
IanS
J'ai ajouté une meilleure réponse. Veuillez marquer ma réponse comme acceptée.
IanS

Réponses:

657

Ajoutez ceci au délégué UIWebView:

(modifié pour vérifier le type de navigation. vous pouvez également transmettre des file://demandes qui seraient des liens relatifs)

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    if (navigationType == UIWebViewNavigationTypeLinkClicked ) {
        [[UIApplication sharedApplication] openURL:[request URL]];
        return NO;
    }

    return YES;
}

Version rapide:

func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
        if navigationType == UIWebViewNavigationType.LinkClicked {
            UIApplication.sharedApplication().openURL(request.URL!)
            return false
        }
        return true
    }

Version Swift 3:

func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
    if navigationType == UIWebViewNavigationType.linkClicked {
        UIApplication.shared.openURL(request.url!)
        return false
    }
    return true
}

Version Swift 4:

func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebView.NavigationType) -> Bool {
    guard let url = request.url, navigationType == .linkClicked else { return true }
    UIApplication.shared.open(url, options: [:], completionHandler: nil)
    return false
}

Mettre à jour

Comme il openURLest déconseillé dans iOS 10:

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
        if (navigationType == UIWebViewNavigationTypeLinkClicked ) {
            UIApplication *application = [UIApplication sharedApplication];
            [application openURL:[request URL] options:@{} completionHandler:nil];
            return NO;
        }

        return YES;
}
dessiné
la source
dessiné par la suite, cela vous dérangerait de mettre à jour votre réponse en vous référant à la réponse et aux commentaires ci-dessous.
Toby Allen
Comment revenir à l'application une fois que l'utilisateur ferme le navigateur?
Johnny Everson,
3
@ Jhonny Everson: Vous n'avez aucun contrôle sur ce qui se passe après la fermeture d'une application externe (y compris Safari). Si vous souhaitez revenir à votre application lorsque l'utilisateur a fini de naviguer, n'ouvrez pas Safari, utilisez simplement UIWwbView et un bouton "Terminé".
geon
1
Fonctionné comme un charme avec un fichier HTML local.
necixy
1
Je pense que, dans Swift, un switchest préférable pour les types d'énumération
SDJMcHattie
44

Si quelqu'un se demande, la solution de Drawnonward ressemblerait à ceci dans Swift :

func webView(webView: UIWebView!, shouldStartLoadWithRequest request: NSURLRequest!, navigationType: UIWebViewNavigationType) -> Bool {
    if navigationType == UIWebViewNavigationType.LinkClicked {
        UIApplication.sharedApplication().openURL(request.URL)
        return false
    }
    return true
}
DiegoFrings
la source
une idée de la façon de le faire UNIQUEMENT avec des URL qui ont https: // http: // ou mailto :? en utilisant rapide? La question ici a besoin d'une réponse rapide! stackoverflow.com/questions/2532453/…
Jed Grant
@JedGrant swift version now on stackoverflow.com/questions/2532453/…
Carl Sharman
2
assurez-vous d'utiliserUIWebViewDelegate
Jay Mayu
28

Un petit commentaire sur la réponse de user306253: attention à cela, lorsque vous essayez de charger quelque chose dans UIWebView vous-même (c'est-à-dire même à partir du code), cette méthode empêchera que cela se produise.

Ce que vous pouvez faire pour éviter cela (merci Wade), c'est:

if (inType == UIWebViewNavigationTypeLinkClicked) {
    [[UIApplication sharedApplication] openURL:[inRequest URL]];
    return NO;
}

return YES;

Vous pouvez également gérer les types UIWebViewNavigationTypeFormSubmittedet UIWebViewNavigationTypeFormResubmitted.

MonsieurDart
la source
8
+1 J'ai eu le même problème. La solution consistait à vérifier UIWebViewNavigationTypeLinkClicked comme type de demande, PUIS ouvrir l'URL et retourner NON, sinon retourner OUI.
Wade Mueller
Wade vous devriez poster votre commentaire comme réponse
Toby Allen
16

Les autres réponses ont un problème: elles dépendent de l'action que vous effectuez et non du lien lui-même pour décider de le charger dans Safari ou dans la vue Web.

Maintenant, parfois, c'est exactement ce que vous voulez, ce qui est bien; mais d'autres fois, surtout si vous avez des liens d'ancrage dans votre page, vous voulez vraiment n'ouvrir que des liens externes dans Safari, et pas des liens internes. Dans ce cas, vous devez vérifier la URL.hostpropriété de votre demande.

J'utilise ce morceau de code pour vérifier si j'ai un nom d'hôte dans l'URL en cours d'analyse, ou s'il est incorporé en html:

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    static NSString *regexp = @"^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9-]*[a-zA-Z0-9])[.])+([A-Za-z]|[A-Za-z][A-Za-z0-9-]*[A-Za-z0-9])$";
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regexp];

    if ([predicate evaluateWithObject:request.URL.host]) {
        [[UIApplication sharedApplication] openURL:request.URL];
        return NO; 
    } else {
        return YES; 
    }
}

Vous pouvez bien sûr adapter l'expression régulière à vos besoins.

KPM
la source
Remarque: l'expression régulière est dérivée de stackoverflow.com/questions/106179/…
KPM
1
Oui au point sur le filtrage des demandes entrantes, non à l'analyse du nom d'hôte. Une meilleure approche serait de filtrer en fonction du schéma d'URL. Sur iOS 8.4 (simulateur), j'ai utilisé "applewebdata" comme schéma pour les liens d'ancrage, mais cela peut varier selon la version cible.
MandisaW
Bonne idée MandisaW. Pour autoriser les liens d'ancrage, je vérifie le schéma de "fichier"if (request.URL?.scheme != "file")
David Douglas
7

Dans Swift, vous pouvez utiliser le code suivant:

extension YourViewController: UIWebViewDelegate {
    func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebView.NavigationType) -> Bool {
        if let url = request.url, navigationType == UIWebView.NavigationType.linkClicked {
            UIApplication.shared.open(url, options: [:], completionHandler: nil)
            return false
        }
        return true
    }

}

Assurez-vous de vérifier la valeur de l'URL et le navigationType.

Antoine
la source
1

Voici l'équivalent Xamarin iOS de la réponse de drawonward.

class WebviewDelegate : UIWebViewDelegate {
    public override bool ShouldStartLoad (UIWebView webView, NSUrlRequest request, UIWebViewNavigationType navigationType) {
        if (navigationType == UIWebViewNavigationType.LinkClicked) {
            UIApplication.SharedApplication.OpenUrl (request.Url);
            return false;
        }
        return true;
    }
}
Danfordham
la source
1

La réponse acceptée ne fonctionne pas.

Si votre page charge des URL via Javascript, ce navigationTypesera le cas UIWebViewNavigationTypeOther. Ce qui, malheureusement, inclut également des chargements de page d'arrière-plan tels que l'analyse.

Pour détecter la navigation dans les pages, vous devez comparer le [request URL]à [request mainDocumentURL].

Cette solution fonctionnera dans tous les cas:

- (BOOL)webView:(UIWebView *)view shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)type
{
    if ([[request URL] isEqual:[request mainDocumentURL]])
    {
        [[UIApplication sharedApplication] openURL:[request URL]];
        return NO;
    }
    else
    {       
        return YES;
    }
}
IanS
la source
1
Pouvez-vous publier une version Swift 4 de cela?
Amjad
1

Rejet de l'application Remarque:

Enfin UIWbViewest mort et Apple ne l'acceptera plus.

Apple a commencé à envoyer des e-mails à tous les propriétaires d'applications qui utilisent toujours UIWebView:

Utilisation d'API obsolète - Apple cessera d'accepter les soumissions d'applications utilisant des UIWebViewAPI.

Apple prend la confidentialité des utilisateurs très au sérieux et il est évident qu'ils n'autoriseront pas la visualisation Web non sécurisée .

Supprimez donc UIWebView de votre application dès que possible. ne pas utiliser essayer d'utiliser UIWebViewdans une nouvelle application créée et je préfère utiliser WKWebViewsi possible

ITMS-90809: Utilisation obsolète de l'API - Apple cessera d'accepter les soumissions d'applications qui utilisent les API UIWebView. Voir https://developer.apple.com/documentation/uikit/uiwebview pour plus d'informations.

Exemple:

import UIKit
import WebKit

class WebInfoController: UIViewController,WKNavigationDelegate {

    var webView : WKWebView = {
        var webview = WKWebView()
        return webview
    }()

    var _fileName : String!

    override func viewDidLoad() {
        self.view.addSubview(webView)
        webView.fillSuperview()
        let url = Bundle.main.url(forResource: _fileName, withExtension: "html")!
        webView.loadFileURL(url, allowingReadAccessTo: url)
        let request = URLRequest(url: url)
        webView.load(request)
    }


    func webView(webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: NSError) {
        print(error.localizedDescription)
    }
    func webView(webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
        print("Strat to load")
    }
    func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {
        print("finish to load")
    }
}
Nazmul Hasan
la source
0

Dans mon cas, je veux m'assurer qu'absolument tout dans la vue Web ouvre Safari sauf la charge initiale et donc j'utilise ...

- (BOOL)webView:(UIWebView *)inWeb shouldStartLoadWithRequest:(NSURLRequest *)inRequest navigationType:(UIWebViewNavigationType)inType {
     if(inType != UIWebViewNavigationTypeOther) {
        [[UIApplication sharedApplication] openURL:[inRequest URL]];
        return NO;
     }
     return YES;
}
Michael Platt
la source