Pourquoi WKWebView n'ouvre-t-il pas les liens avec target = "_ blank"?

122

WKWebViewn'ouvre pas de liens avec target="_blank"l'attribut «Ouvrir dans une nouvelle fenêtre» dans leur HTML <a href>-Tag.

stk
la source
Veuillez mettre à jour la bonne réponse marquée
Efren

Réponses:

184

Ma solution est d'annuler la navigation et de charger la requête avec loadRequest: again. Ce sera le comportement similaire comme UIWebView qui ouvre toujours une nouvelle fenêtre dans le cadre actuel.

Implémentez le WKUIDelegatedélégué et définissez-le sur _webview.uiDelegate. Puis implémentez:

- (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
la source
4
C'est la bonne réponse. Nous avons essayé la réponse acceptée sans succès. Mais la réponse de @ Cloud fonctionne, et cette réponse sur les forums de développement explique pourquoi.
Christopher Pickslay
5
Cette solution a fonctionné pour moi. N'oubliez pas de définir la UIDelegatepropriété, car cette méthode est déclarée dans WKUIDelegatenot WKNavigationDelegate.
Taketo Sano
1
Ok, je vois que les gens veulent utiliser le WKWebView comme ça. J'ai implémenté la possibilité d'ouvrir target = _blank-Links dans le même WKWebView (comme indiqué ci-dessus) dans mon projet github.com/sticksen/STKWebKitViewController .
stk
5
@ChristopherPickslay lors de l'utilisation de cette méthode avec mon WKWebView, l'URL de la requête est toujours vide, donc la vue Web redirige vers une page blanche vierge, des idées?
Jason Murray
1
Lorsque la demande '_blank' est envoyée à partir d'un formulaire avec des données de publication, je trouve que les données de publication seront perdues !!! De l'aide? @Cloud Wu
Andrew
66

La réponse de @Cloud Xu est la bonne réponse. Juste pour référence, le voici dans 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
la source
2
comment l'écririez-vous si vous vouliez qu'il s'ouvre dans Safari à la place? De plus, à quoi dois-je faire référence dans le contrôleur de vue pour que cela fonctionne?
Jed Grant
1
Pour ouvrir la nouvelle page dans Mobile Safari, consultez cette réponse: stackoverflow.com/a/30604481/558789
Paul Bruneau
1
Cela fonctionne pour les liens, mais ne semble pas détecter les nouvelles fenêtres ouvertes avec JavaScript, c'est-à-dire window.open (url, "_blank")
Crashalot
FYI webView.loadRequest a apparemment été renommé webView.load dans les versions récentes de l'API
Bill Weinman
52

Pour utiliser la dernière version de Swift 4.2+

import WebKit

Prolongez votre cours avec WKUIDelegate

Définir un délégué pour la vue Web

self.webView.uiDelegate = self

Mettre en œuvre la méthode de protocole

func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
    if navigationAction.targetFrame == nil {
        webView.load(navigationAction.request)
    }
    return nil
}
Muhasturk
la source
Pas un vrai doublon. Il existe des différences dans le caractère facultatif des paramètres qui ne sont pas conformes au protocole Swift 4.1 WKUIDelegate dans la réponse à laquelle vous avez lié.
yakattack
Bonjour, Lorsque je charge WKWebview avec l'URL de Pinterest, je n'ai pas pu ouvrir "Continuer avec Facebook". Je peux maintenant cliquer et obtenir les informations sur "Continuer avec Google". Aidez-moi s'il vous plaît.
Nrv
Je suis confronté au même problème avec wkwebview; la connexion et la redirection n'ont pas fonctionné dans mon application. De l'aide ?
Jamshed Alam
1
s'il vous plaît, n'importe qui peut m'aider, j'ai assigné le délégué uiDelegate à wkwebview mais sa méthode de création de vue Web avec configuration n'est pas appelée
chetan panchal
25

Ajoutez-vous en tant que WKNavigationDelegate

_webView.navigationDelegate = self;

et implémentez le code suivant dans le rappel de délégué 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: Ce code provient de mon petit projet STKWebKitViewController, qui enveloppe une interface utilisateur utilisable autour de WKWebView.

stk
la source
1
Il y a une faute de frappe. Un point est manquant entre WKNavigationActionPolicy et Allow
chicken
@stk Comment puis-je savoir si UIApplication va ouvrir le lien dans l'application Safari? S'il s'agit d'un lien Appstore - je dois ouvrir ot dans l'application Appstore, mais s'il s'agit d'une page Web habituelle - je dois l'ouvrir dans le même WKWebView stackoverflow.com/questions/29056854/…
BergP
1
@LeoKoppelkamm Ce n'est pas une faute de frappe, c'est plutôt Objective-C pas Swift. ;)
Ayan Sengupta
1
Cela fonctionne pour les liens, mais ne semble pas détecter les nouvelles fenêtres ouvertes avec JavaScript, c'est-à-direwindow.open(url, "_blank")
Crashalot
@Crashalot Avez-vous trouvé la solution pour window.open?
Sam
14

Si vous avez déjà défini le WKWebView.navigationDelegate

WKWebView.navigationDelegate = soi;

il vous suffit de mettre en œuvre:

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

de cette façon, vous n'avez pas besoin d'implémenter la méthode WKUIDelegate.

Boris Georgiev
la source
1
Cela fonctionne pour les liens, mais ne semble pas détecter les nouvelles fenêtres ouvertes avec JavaScript, c'est-à-dire window.open (url, "_blank")
Crashalot
@Crashalot Avez-vous vérifié cette réponse? stackoverflow.com/questions/33190234/wkwebview-and-window-open
Jose Rego
8

Aucune de ces solutions n'a fonctionné pour moi, j'ai résolu le problème en:

1) Implémentation de WKUIDelegate

@interface ViewController () <WKNavigationDelegate, WKUIDelegate>

2) Définition du délégué UIDelegate de wkWebview

self.wkWebview.UIDelegate = self;

3) Implémentation de la méthode 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
la source
Je reçois juste un SIGABRT en faisant cela. Ça ne marche pas.
user2009449
8

Cloud xuLa réponse de résout mon problème.

Au cas où quelqu'un aurait besoin de la version équivalente de Swift (4.x / 5.0), la voici:

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
}

Bien sûr, vous devez d' webView.uiDelegateabord définir .

Benjamin Wen
la source
7

Je confirme que le code Swift de Bill Weinman est correct. Mais vous devez également mentionner que vous devez également déléguer UIDelegate pour que cela fonctionne, au cas où vous êtes nouveau sur iOS en développement comme moi.

Quelque chose comme ça:

self.webView?.UIDelegate = self

Il y a donc trois endroits où vous devez apporter des modifications.

Oliver Zhang
la source
Tapez votre code, c'est:self.webView.UIDelegate = self
Henrik Petterson
Je ne sais pas si cela est implicite, mais pour que cette ligne de code fonctionne dans iOS 10, vous devez ViewControllerhériter WKUIDelegate. ieclass ViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate
ltrainpr
4

Vous pouvez également pousser un autre contrôleur de vue, ou ouvrir un nouvel onglet, etc.:

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
la source
Est-il nécessaire de définir vc.url? Je ne le configure pas et la vue Web est chargée correctement. De plus, dans mon expérience , je ne suis que voyant createWebViewWithConfigurationappelé quand navigationAction.targetFrameest nil. Pouvez-vous décrire un scénario où cela ne serait pas vrai?
Mason G. Zhwiti
@ MasonG.Zhwiti J'utilisais activement WKWebViews pour un projet l'automne dernier, mais depuis, je n'ai rien fait avec eux - donc je ne peux vraiment pas répondre à votre question. Au moment où j'ai posté ce qui précède, cela fonctionnait. Je ne peux pas comprendre comment cela fonctionne sans définir le vc.url.
David H
Je pense que cela fonctionne sans parce que vous retournez la vue Web que vous avez créée, puis (je suppose) tout ce qui vous a demandé de la créer prend soin d'utiliser la vue Web pour charger l'URL en question.
Mason G. Zhwiti
3

Basé sur la réponse d'Allen Huang

Détails

  • Version Xcode 10.3 (10G8), Swift 5

Cibles

  • détecter les liens avec target=“_blank”
  • push voir le contrôleur avec webView si le contrôleur actuel a navigationController
  • present view controller avec webView dans tous les autres cas

Solution

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

Échantillon complet

Info.plist

ajoutez votre paramètre de sécurité de transport 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

Version 1

entrez la description de l'image ici

Version 2

entrez la description de l'image ici

Vasily Bodnarchuk
la source
salut, après la connexion avec Facebook, comment ouvrir la fenêtre de partage pour le partage Facebook de la même manière? Je me suis connecté avec la création de wkwebview, maintenant l'utilisateur est connecté. Maintenant, comment suivre la fenêtre de partage?
Jamshed Alam
Merci, votre solution sauve ma vie :)
Samrat Pramanik
2

Cela a fonctionné pour moi:

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

Comme vous pouvez le voir, ce que nous faisons ici est simplement d'ouvrir un nouveau webViewavec la nouvelle URL et de contrôler la possibilité d'être fermé, juste si vous avez besoin d'une réponse second webviewà afficher sur le premier.

Aitor Pagán
la source
1

J'ai rencontré des problèmes qui ne peuvent pas être résolus simplement en utilisant webView.load(navigationAction.request). J'utilise donc créer une nouvelle vue Web à faire et cela fonctionne très bien.

//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
la source
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
la source