Warum öffnet WKWebView keine Links mit target = "_ blank"?

122

WKWebViewöffnet keine Links, target="_blank"deren HTML- <a href>Tag das Attribut "In neuem Fenster öffnen" enthält.

stk
quelle
Bitte aktualisieren Sie die richtige Antwort markiert
Efren

Antworten:

184

Meine Lösung besteht darin, die Navigation abzubrechen und die Anforderung erneut mit loadRequest zu laden. Dies wird das ähnliche Verhalten wie UIWebView sein, das im aktuellen Frame immer ein neues Fenster öffnet.

Implementieren Sie den WKUIDelegateDelegaten und setzen Sie ihn auf _webview.uiDelegate. Dann implementieren Sie:

- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
{
  if (!navigationAction.targetFrame.isMainFrame) {
    [webView loadRequest:navigationAction.request];
  }

  return nil;
}
Cloud Xu
quelle
4
Dies ist die richtige Antwort. Wir haben die akzeptierte Antwort ohne Erfolg versucht. Aber die Antwort von @ Cloud funktioniert, und diese Antwort in den Entwicklerforen erklärt, warum.
Christopher Pickslay
5
Diese Lösung hat bei mir funktioniert. Vergessen Sie nicht, die UIDelegateEigenschaft festzulegen, da diese Methode in WKUIDelegatenot deklariert ist WKNavigationDelegate.
Taketo Sano
1
Ok, ich sehe, dass die Leute das WKWebView so nutzen wollen. Ich habe die Möglichkeit, target = _blank-Links in derselben WKWebView (wie oben gezeigt) in meinem Projekt github.com/sticksen/STKWebKitViewController zu öffnen .
Stk
5
@ChristopherPickslay Bei Verwendung dieser Methode mit meinem WKWebView ist die Anforderungs-URL immer leer, sodass die Webansicht auf eine leere weiße Seite verweist. Irgendwelche Ideen?
Jason Murray
1
Wenn die '_blank'-Anfrage von einem Formular mit Postdaten gesendet wird, finde ich, dass die Postdaten verloren gehen !!! Irgendeine Hilfe? @ Cloud Wu
Andrew
66

Die Antwort von @Cloud Xu ist die richtige Antwort. Nur als Referenz, hier ist es in Swift:

// this handles target=_blank links by opening them in the same view
func webView(webView: WKWebView!, createWebViewWithConfiguration configuration: WKWebViewConfiguration!, forNavigationAction navigationAction: WKNavigationAction!, windowFeatures: WKWindowFeatures!) -> WKWebView! {
    if navigationAction.targetFrame == nil {
        webView.loadRequest(navigationAction.request)
    }
    return nil
}
Bill Weinman
quelle
2
Wie würden Sie es schreiben, wenn Sie möchten, dass es stattdessen in Safari geöffnet wird? Worauf muss ich im View Controller verweisen, damit dies funktioniert?
Jed Grant
1
Um die neue Seite in der mobilen Safari zu öffnen, lesen
Paul Bruneau
1
Dies funktioniert für Links, scheint aber keine neuen Fenster zu erkennen, die mit JavaScript geöffnet wurden, dh window.open (url, "_blank")
Crashalot
FYI webView.loadRequest wurde offenbar in neueren Versionen der API in webView.load umbenannt
Bill Weinman
52

So verwenden Sie die neueste Version von Swift 4.2+

import WebKit

Erweitern Sie Ihre Klasse mit WKUIDelegate

Festlegen des Delegaten für die Webansicht

self.webView.uiDelegate = self

Protokollmethode implementieren

func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
    if navigationAction.targetFrame == nil {
        webView.load(navigationAction.request)
    }
    return nil
}
Muhasturk
quelle
Kein echtes Duplikat. Es gibt Unterschiede in der Optionalität der Parameter, die nicht mit dem Swift 4.1 WKUIDelegate-Protokoll in der von Ihnen verknüpften Antwort übereinstimmen.
Yakattack
Hallo, als ich WKWebview mit Pinterest URL geladen habe, konnte ich "Weiter mit Facebook" nicht öffnen. Jetzt kann ich auf "Continuew with Google" klicken und die Informationen abrufen. Hilf mir bitte.
Nrv
Ich habe das gleiche Problem mit wkwebview; Login und Weiterleitung funktionierten in meiner App nicht. Irgendeine Hilfe ?
Jamshed Alam
1
Bitte jeder kann mir helfen, ich habe Delegierten uiDelegate zu wkwebview zugewiesen, aber seine Methode zum Erstellen einer Webansicht mit Konfiguration wird nicht aufgerufen
Chetan Panchal
25

Fügen Sie sich als WKNavigationDelegate hinzu

_webView.navigationDelegate = self;

und implementieren Sie den folgenden Code im Delegaten-Rückruf entscheidendPolicyForNavigationAction: entscheidungshandler:

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
    //this is a 'new window action' (aka target="_blank") > open this URL externally. If we´re doing nothing here, WKWebView will also just do nothing. Maybe this will change in a later stage of the iOS 8 Beta
    if (!navigationAction.targetFrame) { 
        NSURL *url = navigationAction.request.URL;
        UIApplication *app = [UIApplication sharedApplication];
        if ([app canOpenURL:url]) {
            [app openURL:url];
        }
    }
    decisionHandler(WKNavigationActionPolicyAllow);
}

PS: Dieser Code stammt aus meinem kleinen Projekt STKWebKitViewController, das eine verwendbare Benutzeroberfläche um WKWebView herum umschließt.

stk
quelle
1
Es gibt einen Tippfehler. Ein Punkt fehlt zwischen WKNavigationActionPolicy und Allow
Huhn
@stk Wie kann ich verstehen, ob UIApplication einen Link in der Safari-App öffnen wird? Wenn es sich um einen Appstore-Link handelt - ich muss ot in der Appstore-App öffnen, aber wenn es sich um eine übliche Webseite handelt - muss ich ihn in derselben WKWebView stackoverflow.com/questions/29056854/… öffnen
BergP
1
@LeoKoppelkamm Es ist kein Tippfehler, sondern Objective-C, nicht Swift. ;)
Ayan Sengupta
1
Dies funktioniert für Links, scheint aber keine neuen Fenster zu erkennen, die mit JavaScript geöffnet wurden, dhwindow.open(url, "_blank")
Crashalot
@Crashalot Haben Sie die Lösung für window.open gefunden?
Sam
14

Wenn Sie das WKWebView.navigationDelegate bereits festgelegt haben

WKWebView.navigationDelegate = self;

Sie müssen nur implementieren:

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
    BOOL shouldLoad = [self shouldStartLoadWithRequest:navigationAction.request]; // check the url if necessary

    if (shouldLoad && navigationAction.targetFrame == nil) {
        // WKWebView ignores links that open in new window
        [webView loadRequest:navigationAction.request];
    }

    // always pass a policy to the decisionHandler
    decisionHandler(shouldLoad ? WKNavigationActionPolicyAllow : WKNavigationActionPolicyCancel);
}

Auf diese Weise müssen Sie die WKUIDelegate-Methode nicht implementieren.

Boris Georgiev
quelle
1
Dies funktioniert für Links, scheint aber keine neuen Fenster zu erkennen, die mit JavaScript geöffnet wurden, dh window.open (url, "_blank")
Crashalot
@Crashalot Hast du diese Antwort überprüft? stackoverflow.com/questions/33190234/wkwebview-and-window-open
Jose Rego
8

Keine dieser Lösungen hat bei mir funktioniert. Ich habe das Problem gelöst durch:

1) Implementierung von WKUIDelegate

@interface ViewController () <WKNavigationDelegate, WKUIDelegate>

2) Festlegen des UIDelegate-Delegaten von wkWebview

self.wkWebview.UIDelegate = self;

3) Implementieren der Methode createWebViewWithConfiguration

- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {

if (!navigationAction.targetFrame.isMainFrame) {
    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
    [[UIApplication sharedApplication] openURL:[navigationAction.request URL]];
}
return nil;  }
Ugo Marinelli
quelle
Ich bekomme dabei nur ein SIGABRT. Es funktioniert nicht.
user2009449
8

Cloud xuDie Antwort löst mein Problem.

Falls jemand die entsprechende Swift-Version (4.x / 5.0) benötigt, hier ist sie:

func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
    if let frame = navigationAction.targetFrame,
        frame.isMainFrame {
        return nil
    }
    // for _blank target or non-mainFrame target
    webView.load(navigationAction.request)
    return nil
}

Natürlich muss man webView.uiDelegatezuerst einstellen .

Benjamin Wen
quelle
7

Ich bestätige, dass der Swift-Code von Bill Weinman korrekt ist. Aber ich muss erwähnen, dass Sie auch UIDelegate delegieren müssen, damit es funktioniert, falls Sie neu in der iOS-Entwicklung wie ich sind.

Etwas wie das:

self.webView?.UIDelegate = self

Es gibt also drei Stellen, an denen Sie Änderungen vornehmen müssen.

Oliver Zhang
quelle
self.webView.UIDelegate = self
Henrik Petterson
Ich bin mir nicht sicher, ob dies impliziert ist, aber damit diese Codezeile in iOS 10 funktioniert, müssen Sie ViewControllersie erben WKUIDelegate. dhclass ViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate
ltrainpr
4

Sie können auch einen anderen Ansichts-Controller drücken oder eine neue Registerkarte öffnen usw.:

func webView(webView: WKWebView, createWebViewWithConfiguration configuration: WKWebViewConfiguration, forNavigationAction navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
    var wv: WKWebView?

    if navigationAction.targetFrame == nil {
        if let vc = self.storyboard?.instantiateViewControllerWithIdentifier("ViewController")  as? ViewController {
            vc.url = navigationAction.request.URL
            vc.webConfig = configuration
            wv = vc.view as? WKWebView

            self.navigationController?.pushViewController(vc, animated: true)
        }
    }

    return wv
}
David H.
quelle
Ist es notwendig einzustellen vc.url? Ich stelle es nicht ein und die Webansicht wird ordnungsgemäß geladen. Außerdem sehe ich meiner Erfahrung nach nur createWebViewWithConfigurationangerufen, wenn es navigationAction.targetFrameist nil. Können Sie ein Szenario beschreiben, in dem dies nicht der Fall wäre?
Mason G. Zhwiti
@ MasonG.Zhwiti Ich habe WKWebViews im letzten Herbst aktiv für ein Projekt verwendet, aber seitdem nichts mehr damit gemacht - daher kann ich Ihre Frage wirklich nicht beantworten. Zu der Zeit, als ich das oben Gesagte gepostet habe, hat es funktioniert. Ich kann mir nicht vorstellen, wie es funktioniert, ohne die vc.url einzustellen.
David H
Ich denke, es funktioniert ohne, weil Sie die von Ihnen erstellte Webansicht zurückgeben, und dann (ich vermute), was auch immer Sie zum Erstellen aufgefordert hat, wird die Webansicht zum Laden der betreffenden URL verwendet.
Mason G. Zhwiti
3

Basierend auf Allen Huang Antwort

Einzelheiten

  • Xcode Version 10.3 (10G8), Swift 5

Ziele

  • Links erkennen mit target=“_blank”
  • push View Controller mit WebView, wenn der aktuelle Controller hat navigationController
  • present View Controller mit WebView in allen anderen Fällen

Lösung

webView.uiDelegate = self

// .....

extension ViewController: WKUIDelegate {
    func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
        guard   navigationAction.targetFrame == nil,
                let url =  navigationAction.request.url else { return nil }
        let vc = ViewController(url: url, configuration: configuration)
        if let navigationController = navigationController {
            navigationController.pushViewController(vc, animated: false)
            return vc.webView
        }
        present(vc, animated: true, completion: nil)
        return nil
    }
}

Vollständige Probe

Info.plist

Fügen Sie Ihre Info.plist-Transportsicherheitseinstellung hinzu

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

ViewController

import UIKit
import WebKit

class ViewController: UIViewController {

    private lazy var url = URL(string: "https://www.w3schools.com/html/tryit.asp?filename=tryhtml_links_target")!
    private weak var webView: WKWebView!

    init (url: URL, configuration: WKWebViewConfiguration) {
        super.init(nibName: nil, bundle: nil)
        self.url = url
        navigationItem.title = ""
    }

    required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }

    override func viewDidLoad() {
        super.viewDidLoad()
        initWebView()
        webView.loadPage(address: url)
    }

    private func initWebView() {
        let webView = WKWebView(frame: .zero, configuration: WKWebViewConfiguration())
        view.addSubview(webView)
        self.webView = webView
        webView.navigationDelegate = self
        webView.uiDelegate = self
        webView.translatesAutoresizingMaskIntoConstraints = false
        webView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
        webView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true
        webView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
        webView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
    }
}

extension ViewController: WKNavigationDelegate {
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        guard let host = webView.url?.host else { return }
        navigationItem.title = host
    }
}

extension ViewController: WKUIDelegate {
    func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
        guard   navigationAction.targetFrame == nil,
                let url =  navigationAction.request.url else { return nil }
        let vc = ViewController(url: url, configuration: configuration)
        if let navigationController = navigationController {
            navigationController.pushViewController(vc, animated: false)
            return vc.webView
        }
        present(vc, animated: true, completion: nil)
        return nil
    }
}

extension WKWebView {
    func loadPage(address url: URL) { load(URLRequest(url: url)) }
    func loadPage(address urlString: String) {
        guard let url = URL(string: urlString) else { return }
        loadPage(address: url)
    }
}

Storyboards

Version 1

Geben Sie hier die Bildbeschreibung ein

Version 2

Geben Sie hier die Bildbeschreibung ein

Wassili Bodnarchuk
quelle
Hallo, wie kann ich nach dem Login mit Facebook das Freigabefenster für Facebook Share auf ähnliche Weise öffnen? Ich habe mich mit dem Erstellen einer neuen wkwebview angemeldet, jetzt ist der Benutzer angemeldet. Wie kann ich nun das Freigabefenster verfolgen?
Jamshed Alam
Vielen Dank, Ihre Lösung rettet mein Leben :)
Samrat Pramanik
2

Das hat bei mir funktioniert:

-(WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {

if (!navigationAction.targetFrame.isMainFrame) {


    WKWebView *newWebview = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
    newWebview.UIDelegate = self;
    newWebview.navigationDelegate = self;
    [newWebview loadRequest:navigationAction.request];
    self.view = newWebview;

    return  newWebview;
}

return nil;
}

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

    decisionHandler(WKNavigationActionPolicyAllow);
}

- (void)webViewDidClose:(WKWebView *)webView {
    self.view = self.webView;
}

Wie Sie sehen können, öffnen wir hier nur eine neue webViewmit der neuen URL und steuern die Möglichkeit des Schließens, nur wenn Sie eine Antwort darauf benötigen second webview, die auf der ersten angezeigt wird.

Aitor Pagán
quelle
1

Ich bin auf einige Probleme gestoßen, die nicht einfach mit gelöst werden können webView.load(navigationAction.request). Also erstelle ich ein neues WebView und es funktioniert einwandfrei.

//MARK:- WKUIDelegate
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
    NSLog(#function)

    if navigationAction.targetFrame == nil {
        NSLog("=> Create a new webView")

        let webView = WKWebView(frame: self.view.bounds, configuration: configuration)
        webView.uiDelegate = self
        webView.navigationDelegate = self

        self.webView = webView

        return webView
    }
    return nil
}
Allen Huang
quelle
0
**Use following function to create web view**

func initWebView(configuration: WKWebViewConfiguration) 
{
        let webView = WKWebView(frame: UIScreen.main.bounds, configuration: configuration)
        webView.uiDelegate = self
        webView.navigationDelegate = self
        view.addSubview(webView)
        self.webView = webView
    }

**In View Did Load:**

 if webView == nil { initWebView(configuration: WKWebViewConfiguration()) }
   webView?.load(url: url1)


**WKUIDelegate Method need to be implemented**

extension WebViewController: WKUIDelegate {

    func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
        // push new screen to the navigation controller when need to open url in another "tab"
        print("url:\(String(describing: navigationAction.request.url?.absoluteString))")
        if let url = navigationAction.request.url, navigationAction.targetFrame == nil {
            let viewController = WebViewController()
            viewController.initWebView(configuration: configuration)
            viewController.url1 = url
            DispatchQueue.main.async { [weak self] in
                self?.navigationController?.pushViewController(viewController, animated: true)
            }
            return viewController.webView
        }

        return nil
    }
}

extension WKWebView 

{
    func load(url: URL) { load(URLRequest(url: url)) }
}
Jayant Rawat
quelle