Perché WKWebView non apre i collegamenti con target = "_ blank"?


122

WKWebViewnon apre alcun collegamento che abbia target="_blank"l'attributo aka "Apri in una nuova finestra" nel tag HTML <a href>.


si prega di aggiornare la risposta corretta contrassegnata
Efren

Risposte:


184

La mia soluzione è annullare la navigazione e caricare la richiesta con loadRequest: di nuovo. Questo avverrà con un comportamento simile a UIWebView che apre sempre una nuova finestra nel frame corrente.

Implementa il WKUIDelegatedelegato e impostalo su _webview.uiDelegate. Quindi implementare:

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

  return nil;
}

4
Questa è la risposta corretta. Abbiamo provato la risposta accettata senza successo. Ma la risposta di @ Cloud funziona e questa risposta sui forum di sviluppo spiega perché.
Christopher Pickslay

5
Questa soluzione ha funzionato per me. Non dimenticare di impostare la UIDelegateproprietà, poiché questo metodo è dichiarato in WKUIDelegatenot WKNavigationDelegate.
Taketo Sano

1
Ok, vedo che le persone vogliono usare WKWebView in questo modo. Ho implementato la possibilità di aprire target = _blank-Links nello stesso WKWebView (come mostrato sopra) nel mio progetto github.com/sticksen/STKWebKitViewController .
stk

5
@ChristopherPickslay quando si utilizza questo metodo con il mio WKWebView l'URL della richiesta è sempre vuoto, quindi la visualizzazione web indirizza a una pagina bianca vuota, qualche idea?
Jason Murray

1
Quando la richiesta '_blank' viene inviata da un modulo con i dati del post, trovo che i dati del post andranno persi !!! Qualsiasi aiuto? @Cloud Wu
Andrew

66

La risposta di @Cloud Xu è la risposta corretta. Solo per riferimento, eccolo 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
}

2
come lo scriveresti se invece volessi aprirlo in Safari? Inoltre a cosa devo fare riferimento nel controller della vista per farlo funzionare?
Jed Grant

1
Per aprire la nuova pagina in Safari mobile, vedere questa risposta: stackoverflow.com/a/30604481/558789
Paul Bruneau,

1
Funziona per i link, ma non sembra rilevare nuove finestre aperte con JavaScript, ad esempio window.open (url, "_blank")
Crashalot

FYI webView.loadRequest è stato apparentemente rinominato webView.load nelle versioni recenti dell'API
Bill Weinman,

52

Per utilizzare l'ultima versione di Swift 4.2+

import WebKit

Estendi la tua classe con WKUIDelegate

Imposta delegato per visualizzazione web

self.webView.uiDelegate = self

Implementare il metodo del protocollo

func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
    if navigationAction.targetFrame == nil {
        webView.load(navigationAction.request)
    }
    return nil
}

Non un vero duplicato. Ci sono differenze nell'opzionalità dei parametri che non sono conformi al protocollo Swift 4.1 WKUIDelegate nella risposta a cui ti sei collegato.
yakattack

Ciao, durante il caricamento di WKWebview con l'URL di Pinterest, non sono riuscito ad aprire "Continua con Facebook". Ora posso fare clic e ottenere le informazioni su "Continua con Google". Aiutami per favore.
Nrv

Sto affrontando lo stesso problema con wkwebview; login e reindirizzamento non hanno funzionato nella mia app. Qualsiasi aiuto ?
Jamshed Alam

1
per favore chiunque può darmi una mano, ho assegnato il delegato uiDelegate a wkwebview ma il suo metodo di creazione della vista web con configurazione non viene chiamato
chetan panchal

25

Aggiungi te stesso come WKNavigationDelegate

_webView.navigationDelegate = self;

e implementare il codice seguente nel callback delegato decidePolicyForNavigationAction: decisionHandler:

- (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: questo codice proviene dal mio piccolo progetto STKWebKitViewController, che racchiude un'interfaccia utente utilizzabile attorno a WKWebView.


1
C'è un errore di battitura. Manca un punto tra WKNavigationActionPolicy e Allow
pollo

@stk Come posso capire se UIApplication aprirà il collegamento nell'app Safari? Se si tratta di Appstore collegamento - Ho bisogno di aprire ot in app AppStore, ma se si è soliti pagina web - ho bisogno di aprirlo nella stessa WKWebView stackoverflow.com/questions/29056854/...
BergP

1
@LeoKoppelkamm Non è un errore di battitura piuttosto è Objective-C non Swift. ;)
Ayan Sengupta

1
Funziona per i collegamenti, ma non sembra rilevare nuove finestre aperte con JavaScript, ad esempiowindow.open(url, "_blank")
Crashalot

@Crashalot Hai trovato la soluzione per window.open?
Sam

14

Se hai già impostato WKWebView.navigationDelegate

WKWebView.navigationDelegate = self;

devi solo implementare:

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

in questo modo non è necessario implementare il metodo WKUIDelegate.


1
Funziona per i link, ma non sembra rilevare nuove finestre aperte con JavaScript, ad esempio window.open (url, "_blank")
Crashalot

@Crashalot Hai controllato questa risposta? stackoverflow.com/questions/33190234/wkwebview-and-window-open
Jose Rego

8

Nessuna di queste soluzioni ha funzionato per me, ho risolto il problema:

1) Implementazione di WKUIDelegate

@interface ViewController () <WKNavigationDelegate, WKUIDelegate>

2) Impostazione del delegato UIDelegate di wkWebview

self.wkWebview.UIDelegate = self;

3) Implementazione del metodo 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;  }

Ricevo solo un SIGABRT quando lo faccio. Non funziona.
user2009449

8

Cloud xuLa risposta di risolve il mio problema.

Nel caso in cui qualcuno abbia bisogno della versione equivalente di Swift (4.x / 5.0), eccola:

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
}

Ovviamente devi webView.uiDelegateprima impostare .


7

Confermo che il codice Swift di Bill Weinman è corretto. Ma è necessario menzionare che devi anche delegare UIDelegate affinché funzioni, nel caso in cui tu sia nuovo per iOS che si sviluppa come me.

Qualcosa come questo:

self.webView?.UIDelegate = self

Quindi ci sono tre posti in cui devi apportare modifiche.


Digita il tuo codice, è:self.webView.UIDelegate = self
Henrik Petterson

Non sono sicuro che sia implicito, ma affinché questa riga di codice funzioni in iOS 10 devi ViewControllerereditare WKUIDelegate. ieclass ViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate
ltrainpr

4

Puoi anche spingere un altro controller di visualizzazione o aprire una nuova scheda, ecc:

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
}

È necessario impostare vc.url? Non lo sto impostando e la visualizzazione Web viene caricata correttamente. Inoltre, nella mia esperienza vedo createWebViewWithConfigurationchiamato solo quando navigationAction.targetFrameè nil. Puoi descrivere uno scenario in cui questo non sarebbe vero?
Mason G. Zhwiti

@ MasonG.Zhwiti Lo scorso autunno stavo usando attivamente WKWebViews per un progetto, ma da allora non ho fatto nulla con loro, quindi non posso davvero rispondere alla tua domanda. Al momento in cui ho pubblicato quanto sopra ha funzionato. Non riesco a capire come funziona senza impostare vc.url però.
David H

Penso che funzioni senza perché stai restituendo la visualizzazione Web che hai creato, quindi (immagino) qualunque cosa ti abbia chiesto di creare si occupa di utilizzare la visualizzazione Web per caricare l'URL in questione.
Mason G. Zhwiti

3

Basato sulla risposta di Allen Huang

Dettagli

  • Versione Xcode 10.3 (10G8), Swift 5

obiettivi

  • rilevare collegamenti con target=“_blank”
  • push visualizzare il controller con webView se il controller corrente ha navigationController
  • present Visualizza controller con webView in tutti gli altri casi

Soluzione

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

Campione completo

Info.plist

aggiungi nelle impostazioni di sicurezza del trasporto di Info.plist

<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

Versione 1

inserisci qui la descrizione dell'immagine

Versione 2

inserisci qui la descrizione dell'immagine


ciao, dopo il login con facebook, come aprire la finestra di condivisione per la condivisione su facebook in modo simile? Ho effettuato l'accesso con la creazione di un nuovo wkwebview, ora l'utente è connesso. Ora come monitorare la finestra di condivisione?
Jamshed Alam

Grazie, la tua soluzione mi ha salvato la vita :)
Samrat Pramanik

2

Questo ha funzionato per me:

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

Come puoi vedere, quello che facciamo qui è solo aprire un nuovo webViewcon il nuovo URL e controllare la possibilità di essere chiuso, solo se hai bisogno di una risposta da second webviewvisualizzare sul primo.


1

Ho riscontrato alcuni problemi che non possono essere risolti semplicemente utilizzando webView.load(navigationAction.request). Quindi uso creare un nuovo webView da fare e funziona bene.

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

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)) }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.