UIWebView abre enlaces en Safari

304

Tengo un UIWebView muy simple con contenido de mi paquete de aplicaciones. Me gustaría que cualquier enlace en la vista web se abra en Safari en lugar de en la vista web. es posible?

David Beck
fuente
La respuesta aceptada a continuación no funcionará en todos los casos.
IanS
He agregado una mejor respuesta. Por favor marque mi respuesta como la aceptada.
IanS

Respuestas:

657

Agregue esto al delegado UIWebView:

(editado para verificar el tipo de navegación. también puede pasar file://solicitudes que serían enlaces relativos)

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

    return YES;
}

Versión rápida:

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

Versión 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
}

Versión 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
}

Actualizar

Como openURLha quedado en desuso en 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;
}
dibujado hacia adelante
fuente
dibujado hacia adelante, ¿le importaría actualizar su respuesta con referencia a la respuesta y los comentarios a continuación?
Toby Allen
¿Cómo volver a la aplicación una vez que el usuario cierra el navegador?
Johnny Everson
3
@ Jhonny Everson: No tienes control sobre lo que sucede después de que se cierra cualquier aplicación externa (incluido Safari). Si desea volver a su aplicación cuando el usuario haya terminado de navegar, no abra Safari, solo use UIWwbView y un botón "Listo".
geon
1
Funcionó como un encanto con el archivo HTML local.
necixy
1
Creo que, en Swift, a switches preferible para los tipos de enumeración
SDJMcHattie
44

Si alguien se pregunta, la solución de Drawnonward se vería así en 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
fuente
¿Alguna idea de cómo hacer esto SOLO con URLS que tengan https: // http: // o mailto :? usando swift? ¡La pregunta aquí necesita una respuesta rápida! stackoverflow.com/questions/2532453/…
Jed Grant
Versión rápida de @JedGrant ahora en stackoverflow.com/questions/2532453/…
Carl Sharman
2
asegúrese de usarUIWebViewDelegate
Jay Mayu
28

Un comentario rápido a la respuesta del usuario 306253: tenga cuidado con esto, cuando intente cargar algo en UIWebView (es decir, incluso desde el código), este método evitará que suceda.

Lo que puede hacer para evitar esto (gracias Wade) es:

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

return YES;

También es posible que desee manejar los tipos UIWebViewNavigationTypeFormSubmittedy UIWebViewNavigationTypeFormResubmitted.

MonsieurDart
fuente
8
+1 Tuve el mismo problema. La solución fue verificar UIWebViewNavigationTypeLinkClicked como el tipo de solicitud, ENTONCES abrir la URL y devolver NO, de lo contrario devolver SÍ.
Wade Mueller
Wade deberías publicar tu comentario como respuesta
Toby Allen
16

Las otras respuestas tienen un problema: se basan en la acción que realiza y no en el enlace en sí para decidir si cargarlo en Safari o en la vista web.

Ahora a veces esto es exactamente lo que quieres, lo cual está bien; pero en otras ocasiones, especialmente si tiene enlaces de anclaje en su página, realmente desea abrir solo enlaces externos en Safari, y no enlaces internos. En ese caso, debe verificar la URL.hostpropiedad de su solicitud.

Utilizo ese fragmento de código para verificar si tengo un nombre de host en la URL que se está analizando o si está incrustado 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; 
    }
}

Por supuesto, puede adaptar la expresión regular a sus necesidades.

KPM
fuente
Nota: la expresión regular se deriva de stackoverflow.com/questions/106179/…
KPM
1
Sí, hasta el punto de filtrar las solicitudes entrantes, no al análisis del nombre de host. Un mejor enfoque sería filtrar según el esquema de URL. En iOS 8.4 (simulador), utilicé "applewebdata" como esquema para enlaces de anclaje, pero eso puede variar con la versión de destino.
MandisaW
Buena idea MandisaW. Para permitir enlaces de anclaje, verifico el esquema de "archivo"if (request.URL?.scheme != "file")
David Douglas
7

En Swift puedes usar el siguiente código:

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
    }

}

Asegúrese de verificar el valor de URL y el tipo de navegación.

Antoine
fuente
1

Aquí está el equivalente de Xamarin para iOS de la respuesta de drawnonward.

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
fuente
1

La respuesta aceptada no funciona.

Si su página carga URL a través de Javascript, navigationTypeserá UIWebViewNavigationTypeOther. Que, desafortunadamente, también incluye cargas de páginas de fondo, como análisis.

Para detectar la navegación de la página, debe compararla [request URL]con [request mainDocumentURL].

Esta solución funcionará en todos los casos:

- (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
fuente
1
¿Puedes publicar una versión de Swift 4 de esto?
Amjad el
1

Nota de rechazo de la aplicación:

Finalmente UIWbViewestá muerto y Apple ya no lo aceptará.

Apple comenzó a enviar correos electrónicos a todos los propietarios de aplicaciones que todavía usan UIWebView:

Uso de API en desuso: Apple dejará de aceptar envíos de aplicaciones que usan UIWebViewAPI.

Apple se toma muy en serio la privacidad del usuario y es obvio que no permitirán una vista web insegura .

Entonces, elimine UIWebView de su aplicación lo antes posible. no use intentar usar UIWebViewen la nueva aplicación creada y prefiero usar WKWebViewsi es posible

ITMS-90809: Uso de API obsoleto: Apple dejará de aceptar envíos de aplicaciones que usen API UIWebView. Consulte https://developer.apple.com/documentation/uikit/uiwebview para obtener más información.

Ejemplo:

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
fuente
0

En mi caso, quiero asegurarme de que absolutamente todo en la vista web abra Safari, excepto la carga inicial, así que uso ...

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