Por que o WKWebView não está abrindo links com target = “_ blank”?


122

WKWebViewnão abre nenhum link que tenha target="_blank"também conhecido como atributo 'Abrir em uma nova janela' em sua <a href>tag HTML .


atualize a resposta correta marcada
— Efren

Respostas:


184

Minha solução é cancelar a navegação e carregar a solicitação com loadRequest: novamente. Isso virá com o comportamento semelhante ao UIWebView, que sempre abre uma nova janela no quadro atual.

Implemente o WKUIDelegatedelegado e defina-o como _webview.uiDelegate. Em seguida, implemente:

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

  return nil;
}

4
Essa é a resposta correta. Tentamos a resposta aceita sem sucesso. Mas a resposta da @ Cloud funciona, e essa resposta nos fóruns de desenvolvimento explica o porquê.
— Christopher Pickslay

5
Essa solução funcionou para mim. Não se esqueça de definir a UIDelegatepropriedade, já que este método é declarado em WKUIDelegatenot WKNavigationDelegate.
— Taketo Sano

1
Ok, vejo que as pessoas querem usar o WKWebView assim. Implementei a possibilidade de abrir target = _blank-Links no mesmo WKWebView (como mostrado acima) em meu projeto github.com/sticksen/STKWebKitViewController .
— stk

5
@ChristopherPickslay ao usar este método com o meu WKWebView o url do pedido está sempre em branco, então o webview direciona para uma página em branco, alguma ideia?
— Jason Murray

1
Quando a solicitação '_blank' é enviada de um formulário com dados de postagem, acho que os dados de postagem serão perdidos !!! Qualquer ajuda? @Cloud Wu
— Andrew

66

A resposta de @Cloud Xu é a resposta correta. Apenas para referência, aqui está em 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
como você o escreveria se preferisse abri-lo no Safari? Além disso, o que preciso fazer referência no controlador de visualização para fazer isso funcionar?
— Jed Grant

1
Para fazer com que a nova página seja aberta no safari móvel, veja esta resposta: stackoverflow.com/a/30604481/558789
— Paul Bruneau

1
Isso funciona para links, mas não parece detectar novas janelas abertas com JavaScript, ou seja, window.open (url, "_blank")
— Crashalot

Para sua informação, webView.loadRequest foi aparentemente renomeado para webView.load em versões recentes da API
— Bill Weinman

52

Para usar a versão mais recente do Swift 4.2+

import WebKit

Estenda sua aula com WKUIDelegate

Definir delegado para webview

self.webView.uiDelegate = self

Implementar método de protocolo

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

Não é uma duplicata real. Existem diferenças na opcionalidade dos parâmetros que não estão em conformidade com o protocolo Swift 4.1 WKUIDelegate na resposta que você vinculou.
— yakattack de

Olá, Quando estou carregando o WKWebview com o URL do Pinterest, não consigo abrir "Continuar com o Facebook". Agora posso clicar e obter as informações sobre "Continuar com o Google". Me ajude por favor.
— Nrv

Estou enfrentando o mesmo problema com wkwebview; login e redirecionamento não funcionaram no meu aplicativo. Qualquer ajuda ?
— Jamshed Alam

1
por favor, alguém pode me ajudar, eu designei o delegado uiDelegate para wkwebview, mas seu método de criar visualização da web com configuração não está sendo chamado
— chetan panchal

25

Adicione-se como WKNavigationDelegate

_webView.navigationDelegate = self;

e implemente o seguinte código no retorno de chamada delegado 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: Este código é do meu pequeno projeto STKWebKitViewController, que envolve uma IU utilizável em torno de WKWebView.


1
Ocorreu um erro de digitação. Um ponto está faltando entre WKNavigationActionPolicy e Permitir
— frango

@stk Como posso saber se o UIApplication vai abrir um link no aplicativo Safari? Se for um link da Appstore - preciso abri-lo no aplicativo Appstore, mas se for uma página da web normal - preciso abri-lo no mesmo WKWebView stackoverflow.com/questions/29056854/…
— BergP

1
@LeoKoppelkamm Não é um erro de digitação, mas é Objective-C e não Swift. ;)
— Ayan Sengupta

1
Isso funciona para links, mas não parece detectar novas janelas abertas com JavaScript, ou seja,window.open(url, "_blank")
— Crashalot

@Crashalot Você encontrou a solução para window.open?
— Sam

14

Se você já definiu o WKWebView.navigationDelegate

WKWebView.navigationDelegate = self;

você só precisa implementar:

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

dessa forma, você não precisa implementar o método WKUIDelegate.


1
Isso funciona para links, mas não parece detectar novas janelas abertas com JavaScript, ou seja, window.open (url, "_blank")
— Crashalot


8

Nenhuma dessas soluções funcionou para mim, resolvi o problema ao:

1) Implementando WKUIDelegate

@interface ViewController () <WKNavigationDelegate, WKUIDelegate>

2) Definir o delegado UIDelegate do wkWebview

self.wkWebview.UIDelegate = self;

3) Implementação do método 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;  }

Acabei de receber um SIGABRT ao fazer isso. Não funciona.
— user2009449

8

Cloud xua resposta de resolve meu problema.

Caso alguém precise da versão equivalente do Swift (4.x / 5.0), aqui está:

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
}

Claro que você tem que definir webView.uiDelegateprimeiro.


7

Confirmo que o código Swift de Bill Weinman está correto. Mas preciso mencionar que você também precisa delegar o UIDelegate para que funcione, caso você seja novo no desenvolvimento de iOS como eu.

Algo assim:

self.webView?.UIDelegate = self

Portanto, há três lugares em que você precisa fazer alterações.


Digite seu código, é:self.webView.UIDelegate = self
— Henrik Petterson

Não tenho certeza se está implícito, mas para que essa linha de código funcione no iOS 10, você ViewControllerprecisa herdar WKUIDelegate. ieclass ViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate
— ltrainpr

4

Você também pode enviar outro controlador de visualização ou abrir uma nova guia, 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
}

É necessário definir vc.url? Não estou configurando e a visualização da web está sendo carregada corretamente. Além disso, na minha experiência, só vejo createWebViewWithConfigurationquando navigationAction.targetFrameé chamado nil. Você pode descrever um cenário em que isso não seja verdade?
— Mason G. Zhwiti

@ MasonG.Zhwiti Eu estava usando ativamente WKWebViews para um projeto no outono passado, mas desde então não fiz nada com eles - então realmente não posso responder sua pergunta. No momento em que postei o acima, funcionou. Não consigo entender como isso funciona sem definir o vc.url embora.
— David H

Acho que funciona sem, porque você está retornando a visualização da web que criou e, em seguida (estou supondo), o que quer que seja solicitado a você criar se encarrega de usar a visualização da web para carregar a URL em questão.
— Mason G. Zhwiti

3

Com base na resposta de allen huang

Detalhes

  • Xcode versão 10.3 (10G8), Swift 5

Alvos

  • detectar links com target=“_blank”
  • push ver controlador com webView se o controlador atual tiver navigationController
  • present ver controlador com webView em todos os outros casos

Solução

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

Amostra completa

Info.plist

adicione em sua configuração de segurança de transporte 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

Versão 1

insira a descrição da imagem aqui

Versão 2

insira a descrição da imagem aqui


oi, depois de fazer o login com o facebook, como abrir a janela de compartilhamento do facebook de forma semelhante? Fiz login com a criação de novo wkwebview, agora o usuário está conectado. Agora, como controlar a janela de compartilhamento?
— Jamshed Alam

Obrigado, sua solução salve minha vida :)
— Samrat Pramanik

2

Isso funcionou para mim:

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

Como você pode ver, o que fazemos aqui é apenas abrir uma nova webViewcom a nova url e controlar a possibilidade de ser fechada, apenas se precisar que uma resposta dessa second webviewseja exibida na primeira.


1

Encontrei alguns problemas que não podem ser resolvidos apenas com o webView.load(navigationAction.request). Então, eu uso a criação de um novo webView para fazer e funciona bem.

//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)) }
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.