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 .

stk
fonte
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;
}
Cloud Xu
fonte
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
}
Bill Weinman
fonte
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
}
muhasturk
fonte
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.

stk
fonte
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.

Boris Georgiev
fonte
1
Isso funciona para links, mas não parece detectar novas janelas abertas com JavaScript, ou seja, window.open (url, "_blank")
Crashalot
@Crashalot Você verificou esta resposta? stackoverflow.com/questions/33190234/wkwebview-and-window-open
Jose Rego
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;  }
Ugo Marinelli
fonte
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.

Benjamin Wen
fonte
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.

Oliver Zhang
fonte
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
}
David H
fonte
É 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

Vasily Bodnarchuk
fonte
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.

Aitor Pagán
fonte
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
}
Allen Huang
fonte
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
fonte