Mengapa WKWebView tidak membuka tautan dengan target = “_ blank”?

122

WKWebViewtidak membuka tautan apa pun yang memiliki target="_blank"atribut alias 'Buka di Jendela baru' di HTML <a href>-Tag mereka.

stk
sumber
perbarui jawaban yang benar yang ditandai
Efren

Jawaban:

184

Solusi saya adalah membatalkan navigasi dan memuat permintaan dengan loadRequest: lagi. Ini akan menjadi perilaku serupa seperti UIWebView yang selalu membuka jendela baru di bingkai saat ini.

Terapkan WKUIDelegatedelegasi dan setel ke _webview.uiDelegate. Kemudian terapkan:

- (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
sumber
4
Ini jawaban yang benar. Kami mencoba jawaban yang diterima tetapi tidak berhasil. Tetapi jawaban @ Cloud berhasil, dan tanggapan di forum pengembang ini menjelaskan alasannya.
Christopher Pickslay
5
Solusi ini berhasil untuk saya. Jangan lupa untuk menyetel UIDelegateproperti, karena metode ini dideklarasikan dalam WKUIDelegatenot WKNavigationDelegate.
Taketo Sano
1
Oke, saya melihat bahwa orang ingin menggunakan WKWebView seperti ini. Saya menerapkan kemungkinan untuk membuka target = _blank-Links di WKWebView yang sama (seperti yang ditunjukkan di atas) di proyek saya github.com/sticksen/STKWebKitViewController .
stk
5
@ChristopherPickslay saat menggunakan metode ini dengan WKWebView saya, url permintaan selalu kosong, jadi tampilan web mengarahkan ke halaman putih kosong, ada ide?
Jason Murray
1
Ketika permintaan '_blank' dikirim dari formulir dengan data posting, saya menemukan data posting akan hilang !!! Ada bantuan? @Cloud Wu
Andrew
66

Jawaban dari @Cloud Xu adalah jawaban yang benar. Hanya untuk referensi, ini dia di 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
sumber
2
bagaimana Anda akan menulisnya jika Anda ingin membuka di Safari? Juga apa yang perlu saya rujuk dalam pengontrol tampilan agar ini berfungsi?
Jed Grant
1
Untuk membuka halaman baru di safari seluler, lihat jawaban ini: stackoverflow.com/a/30604481/558789
Paul Bruneau
1
Ini berfungsi untuk tautan, tetapi tampaknya tidak mendeteksi jendela baru yang dibuka dengan JavaScript, yaitu, window.open (url, "_blank")
Crashalot
FYI webView.loadRequest rupanya telah berganti nama menjadi webView.load dalam versi terbaru API
Bill Weinman
52

Untuk menggunakan versi terbaru Swift 4.2+

import WebKit

Perluas kelas Anda dengan WKUIDelegate

Tetapkan delegasi untuk tampilan web

self.webView.uiDelegate = self

Menerapkan metode protokol

func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
    if navigationAction.targetFrame == nil {
        webView.load(navigationAction.request)
    }
    return nil
}
muhasturk.dll
sumber
Bukan duplikat asli. Ada perbedaan dalam opsionalitas parameter yang tidak sesuai dengan protokol Swift 4.1 WKUIDelegate dalam jawaban yang Anda tautkan.
yakattack
Hai, Saat saya memuat WKWebview dengan URL Pinterest, saya tidak dapat membuka "Lanjutkan dengan Facebook". Sekarang saya bisa mengklik dan mendapatkan informasi tentang "Lanjutkan dengan Google". Tolong bantu aku.
Nrv
Saya menghadapi masalah yang sama dengan wkwebview; login dan redirect tidak berfungsi di aplikasi saya. Ada Bantuan?
Jamshed Alam
1
tolong ada yang bisa membantu saya, saya telah menugaskan delegasi uiDelegate ke wkwebview tetapi metode membuat tampilan web dengan konfigurasi tidak dipanggil
chetan panchal
25

Tambahkan diri Anda sebagai WKNavigationDelegate

_webView.navigationDelegate = self;

dan implementasikan kode berikut dalam delegasi callback decisionPolicyForNavigationAction: 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: Kode ini berasal dari proyek kecil saya STKWebKitViewController, yang membungkus UI yang dapat digunakan di sekitar WKWebView.

stk
sumber
1
Ada kesalahan ketik. Ada titik yang hilang antara WKNavigationActionPolicy dan Allow
ayam
@stk Bagaimana saya bisa memahami jika UIApplication akan membuka tautan di aplikasi Safari? Jika ini adalah tautan Appstore - Saya perlu membukanya di aplikasi Appstore, tetapi jika ini adalah laman web biasa - Saya perlu membukanya di WKWebView stackoverflow.com/questions/29056854/… yang
BergP
1
@LeoKoppelkamm Ini bukan salah ketik melainkan Objective-C bukan Swift. ;)
Ayan Sengupta
1
Ini berfungsi untuk tautan, tetapi tampaknya tidak mendeteksi jendela baru yang dibuka dengan JavaScript, yaitu,window.open(url, "_blank")
Crashalot
@ Crashalot Apakah Anda menemukan solusi untuk window.open?
Sam
14

Jika Anda sudah menyetel WKWebView.navigationDelegate

WKWebView.navigationDelegate = diri;

Anda hanya perlu menerapkan:

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

dengan cara ini Anda tidak perlu mengimplementasikan metode WKUIDelegate.

Boris Georgiev
sumber
1
Ini berfungsi untuk tautan, tetapi tampaknya tidak mendeteksi jendela baru yang dibuka dengan JavaScript, yaitu, window.open (url, "_blank")
Crashalot
@ Crashalot Sudahkah Anda memeriksa jawaban ini? stackoverflow.com/questions/33190234/wkwebview-and-window-open
Jose Rego
8

Tak satu pun dari solusi itu berhasil untuk saya, saya menyelesaikan masalah dengan:

1) Pelaksana WKUIDelegate

@interface ViewController () <WKNavigationDelegate, WKUIDelegate>

2) Mengatur delegasi UIDelegate dari wkWebview

self.wkWebview.UIDelegate = self;

3) Menerapkan metode 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
sumber
Saya baru saja mendapatkan SIGABRT saat melakukan ini. Itu tidak berhasil.
pengguna2009449
8

Cloud xuJawabannya memecahkan masalah saya.

Jika seseorang membutuhkan versi Swift (4.x / 5.0) yang setara, ini dia:

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
}

Tentu Anda harus mengaturnya webView.uiDelegateterlebih dahulu.

Benjamin Wen
sumber
7

Saya mengonfirmasi bahwa kode Swift Bill Weinman sudah benar. Tetapi perlu disebutkan bahwa Anda juga perlu mendelegasikan UIDelegate agar berfungsi, jika Anda baru mengenal iOS berkembang seperti saya.

Sesuatu seperti ini:

self.webView?.UIDelegate = self

Jadi ada tiga tempat yang Anda perlukan untuk melakukan perubahan.

Oliver Zhang
sumber
Ketik kode Anda, ini:self.webView.UIDelegate = self
Henrik Petterson
Tidak yakin apakah itu tersirat, tetapi agar baris kode ini berfungsi di iOS 10, Anda ViewControllerharus mewarisi WKUIDelegate. yaituclass ViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate
ltrainpr
4

Anda juga dapat mendorong pengontrol tampilan lain, atau membuka tab baru, dll:

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.
sumber
Apakah perlu diatur vc.url? Saya tidak menyetelnya, dan tampilan web dimuat dengan benar. Juga, dalam pengalaman saya saya hanya seeing createWebViewWithConfigurationdipanggil saat navigationAction.targetFrameini nil. Dapatkah Anda menjelaskan skenario yang tidak benar?
Mason G.Zhwiti
@ MasonG.Zhwiti Saya secara aktif menggunakan WKWebViews untuk sebuah proyek musim gugur lalu, tetapi sejak itu saya tidak melakukan apa pun dengan mereka - jadi saya benar-benar tidak dapat menjawab pertanyaan Anda. Pada saat saya memposting di atas, itu berhasil. Saya tidak dapat memahami cara kerjanya tanpa mengatur vc.url sekalipun.
David H
Menurut saya ini berfungsi tanpa karena Anda mengembalikan tampilan web yang Anda buat, dan kemudian (saya menebak) apa pun yang meminta Anda untuk membuatnya, tetap menggunakan tampilan web untuk memuat URL yang dipermasalahkan.
Mason G.Zhwiti
3

Berdasarkan jawaban allen huang

Detail

  • Xcode Versi 10.3 (10G8), Swift 5

Target

  • mendeteksi tautan dengan target=“_blank”
  • push lihat pengontrol dengan webView jika pengontrol saat ini memilikinya navigationController
  • present lihat pengontrol dengan webView dalam semua kasus lainnya

Larutan

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

Sampel lengkap

Info.plist

tambahkan pengaturan keamanan transportasi Info.plist Anda

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

Papan cerita

Versi 1

masukkan deskripsi gambar di sini

Versi 2

masukkan deskripsi gambar di sini

Bodnarchuk dengan mudah
sumber
hai, setelah login dengan facebook, bagaimana cara membuka jendela share untuk share facebook dengan cara yang sama? Saya telah masuk dengan membuat wkwebview baru, sekarang pengguna masuk. Sekarang bagaimana cara melacak jendela berbagi?
Jamshed Alam
Terima kasih, solusi Anda menyelamatkan hidup saya :)
Samrat Pramanik
2

Ini berhasil untuk saya:

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

Seperti yang Anda lihat, apa yang kami lakukan di sini hanyalah membuka yang baru webViewdengan url baru dan mengontrol kemungkinan ditutup, hanya jika Anda memerlukan respons dari itu second webviewuntuk ditampilkan terlebih dahulu.

Aitor Pagán
sumber
1

Saya mengalami beberapa masalah yang tidak dapat diselesaikan hanya dengan menggunakan webView.load(navigationAction.request). Jadi saya menggunakan create a new webView untuk dilakukan dan itu berfungsi dengan baik.

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