UIWebView otwiera linki w Safari

304

Mam bardzo prosty interfejs UIWebView z zawartością pakietu aplikacji. Chciałbym, aby wszystkie linki w widoku internetowym otwierały się w przeglądarce Safari zamiast w widoku internetowym. czy to możliwe?

David Beck
źródło
Akceptowana odpowiedź poniżej nie będzie działać we wszystkich przypadkach.
IanS
Dodałem lepszą odpowiedź. Proszę zaznaczyć moją odpowiedź jako zaakceptowaną.
IanS

Odpowiedzi:

657

Dodaj to do delegata UIWebView:

(edytowane w celu sprawdzenia typu nawigacji. można również przekazywać file://żądania, które byłyby względnymi linkami)

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

    return YES;
}

Wersja szybka:

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

Wersja 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
}

Wersja 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
}

Aktualizacja

Jak openURLjuż nieaktualne w 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;
}
pociągnięty
źródło
w dalszej kolejności, czy mógłbyś zaktualizować swoją odpowiedź w odniesieniu do odpowiedzi i komentarzy poniżej?
Toby Allen
Jak wrócić do aplikacji po zamknięciu przeglądarki przez użytkownika?
Johnny Everson
3
@ Jhonny Everson: Nie masz kontroli nad tym, co dzieje się po zamknięciu dowolnej aplikacji zewnętrznej (w tym Safari). Jeśli chcesz wrócić do aplikacji po zakończeniu przeglądania przez użytkownika, nie otwieraj Safari, wystarczy użyć UIWwbView i przycisku „Gotowe”.
geon
1
Działa jak urok z lokalnym plikiem HTML.
zszywka
1
Myślę, że w Swift switchpreferuje się typy wyliczeniowe
SDJMcHattie
44

Jeśli ktoś się zastanawia, rozwiązanie Drawnonward wyglądałoby tak w 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
źródło
jakiś pomysł, jak to zrobić WYŁĄCZNIE z adresami URL, które mają https: // http: // lub mailto :? używasz szybkiego? Pytanie tutaj wymaga szybkiej odpowiedzi! stackoverflow.com/questions/2532453/…
Jed Grant
@JedGrant szybka wersja teraz na stackoverflow.com/questions/2532453/…
Carl Sharman
2
upewnij się, że użyjeszUIWebViewDelegate
Jay Mayu
28

Jeden szybki komentarz do odpowiedzi user306253: zachowaj ostrożność, gdy spróbujesz samodzielnie załadować coś do UIWebView (tj. Nawet z kodu), ta metoda zapobiegnie temu.

Co możesz zrobić, aby temu zapobiec (dzięki Wade) to:

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

return YES;

Możesz także chcieć obsługiwać typy UIWebViewNavigationTypeFormSubmittedi UIWebViewNavigationTypeFormResubmitted.

MonsieurDart
źródło
8
+1 Miałem ten sam problem. Rozwiązaniem było sprawdzenie UIWebViewNavigationTypeLinkClicked jako typ żądania, następnie otwórz adres URL i zwróć NIE, w przeciwnym razie zwróć TAK.
Wade Mueller,
Wade, powinieneś opublikować swój komentarz jako odpowiedź
Toby Allen
16

Inne odpowiedzi mają jeden problem: polegają na tym, co robisz, a nie na samym łączu, aby zdecydować, czy załadować go w przeglądarce Safari, czy w widoku internetowym.

Czasami jest to dokładnie to, czego chcesz, co jest w porządku; ale w innych przypadkach, szczególnie jeśli masz linki kotwiące na swojej stronie, naprawdę chcesz otwierać tylko zewnętrzne linki w Safari, a nie wewnętrzne. W takim przypadku powinieneś sprawdzićURL.host właściwość swojego żądania.

Używam tego fragmentu kodu, aby sprawdzić, czy mam nazwę hosta w analizowanym adresie URL, czy też jest osadzony 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; 
    }
}

Możesz oczywiście dostosować wyrażenie regularne do swoich potrzeb.

KPM
źródło
Uwaga: wyrażenie regularne pochodzi z stackoverflow.com/questions/106179/…
KPM
1
Tak, do filtrowania przychodzących żądań, nie do analizy nazwy hosta. Lepszym rozwiązaniem byłoby filtrowanie na podstawie schematu URL. Na iOS 8.4 (symulator) dostałem „applewebdata” jako schemat linków zakotwiczenia, ale może się to różnić w zależności od wersji docelowej.
MandisaW
Dobry pomysł MandisaW. Aby zezwolić na linki kotwiczące, sprawdzam schemat „pliku”if (request.URL?.scheme != "file")
David Douglas,
7

W Swift możesz użyć następującego kodu:

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
    }

}

Upewnij się, że sprawdziłeś wartość adresu URL i typ nawigacji.

Antoine
źródło
1

Oto odpowiednik Xamarin iOS dla ciągnionej odpowiedzi.

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
źródło
1

Akceptowana odpowiedź nie działa.

Jeśli Twoja strona ładuje adresy URL za pomocą Javascript, navigationTypebędzie to możliwe UIWebViewNavigationTypeOther. Który niestety obejmuje również ładowanie stron w tle, takie jak analizy.

Aby wykryć nawigację po stronie, musisz porównać [request URL]do [request mainDocumentURL].

To rozwiązanie będzie działać we wszystkich przypadkach:

- (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
źródło
1
Czy możesz opublikować wersję Swift 4?
Amjad
1

Odrzucenie aplikacji Uwaga:

Nareszcie UIWbViewjest martwy i Apple już go nie zaakceptuje.

Apple zaczął wysyłać wiadomości e-mail do wszystkich właścicieli aplikacji, którzy nadal używają UIWebView:

Przestarzałe użycie interfejsu API - Apple przestanie akceptować zgłoszenia aplikacji korzystających z UIWebViewinterfejsów API.

Apple bardzo poważnie podchodzi do kwestii prywatności użytkowników i oczywiste jest, że nie zezwalają na niezabezpieczony podgląd strony .

Więc usuń UIWebView z aplikacji tak szybko, jak to możliwe. nie używaj spróbuj użyć UIWebVieww nowo utworzonej aplikacji i wolę używać, WKWebViewjeśli to możliwe

ITMS-90809: Przestarzałe użycie interfejsu API - Apple przestanie akceptować zgłoszenia aplikacji korzystających z interfejsów API UIWebView. Aby uzyskać więcej informacji, zobacz https://developer.apple.com/documentation/uikit/uiwebview .

Przykład:

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
źródło
0

W moim przypadku chcę się upewnić, że absolutnie wszystko w widoku sieci otwiera Safari oprócz początkowego ładowania, więc używam ...

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