เหตุใด WKWebView จึงไม่เปิดลิงก์ด้วย target =“ _ blank”


122

WKWebViewไม่เปิดลิงก์ใด ๆ ที่มีtarget="_blank"แอตทริบิวต์ "Open in new Window" ใน HTML <a href>-Tag


โปรดอัปเดตคำตอบที่ถูกต้องที่ทำเครื่องหมาย
Efren

คำตอบ:


184

วิธีแก้ปัญหาของฉันคือยกเลิกการนำทางและโหลดคำขอด้วย loadRequest: อีกครั้ง นี่จะเป็นลักษณะการทำงานที่คล้ายกันเช่น UIWebView ที่เปิดหน้าต่างใหม่ในเฟรมปัจจุบัน

ใช้ผู้รับมอบสิทธิ์และตั้งค่าให้WKUIDelegate _webview.uiDelegateจากนั้นใช้:

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

  return nil;
}

4
นี่คือคำตอบที่ถูกต้อง เราลองใช้คำตอบที่ยอมรับแล้ว แต่ไม่ประสบความสำเร็จ แต่คำตอบของ @ Cloud ใช้ได้ผลและคำตอบนี้ในฟอรัม devอธิบายว่าทำไม
Christopher Pickslay

5
วิธีนี้ใช้ได้ผลสำหรับฉัน อย่าลืมตั้งค่าUIDelegateคุณสมบัติเนื่องจากวิธีนี้WKUIDelegateไม่ได้ประกาศWKNavigationDelegateไว้
Taketo Sano

1
โอเคฉันเห็นว่ามีคนอยากใช้ WKWebView แบบนี้ ผมดำเนินการไปได้ที่จะเปิดเป้าหมาย = _ blank ลิงค์ใน WKWebView เดียวกัน (ดังแสดงด้านบน) ในโครงการของฉันgithub.com/sticksen/STKWebKitViewController
STK

5
@ChristopherPickslay เมื่อใช้วิธีนี้กับ WKWebView ของฉัน url คำขอจะว่างเสมอดังนั้น Webview จึงนำไปยังหน้าสีขาวว่างเปล่ามีความคิดอย่างไร
Jason Murray

1
เมื่อคำขอ '_blank' ถูกส่งจากแบบฟอร์มที่มีข้อมูลโพสต์ฉันพบว่าข้อมูลโพสต์จะหายไป !!! ความช่วยเหลือใด ๆ @Cloud Wu
Andrew

66

คำตอบจาก @Cloud Xu คือคำตอบที่ถูกต้อง สำหรับการอ้างอิงนี่คือใน 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
คุณจะเขียนมันอย่างไรถ้าคุณต้องการให้เปิดใน Safari แทน? ฉันต้องอ้างอิงอะไรในตัวควบคุมมุมมองเพื่อให้ใช้งานได้
Jed Grant

1
หากต้องการเปิดหน้าใหม่ในซาฟารีมือถือโปรดดูคำตอบนี้: stackoverflow.com/a/30604481/558789
Paul Bruneau

1
วิธีนี้ใช้ได้กับลิงก์ แต่ดูเหมือนจะตรวจไม่พบหน้าต่างใหม่ที่เปิดด้วย JavaScript เช่น window.open (url, "_blank")
Crashalot

FYI webView.loadRequest ถูกเปลี่ยนชื่อเป็น webView.load ในเวอร์ชันล่าสุดของ API
Bill Weinman

52

หากต้องการใช้ Swift 4.2+ เวอร์ชันล่าสุด

import WebKit

ขยายชั้นเรียนของคุณด้วยWKUIDelegate

ตั้งค่าผู้รับมอบสิทธิ์สำหรับ webview

self.webView.uiDelegate = self

ใช้วิธีโปรโตคอล

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

ไม่ซ้ำกันจริง มีความแตกต่างในตัวเลือกของพารามิเตอร์ที่ไม่เป็นไปตามโปรโตคอล Swift 4.1 WKUIDelegate ในคำตอบที่คุณเชื่อมโยง
yakattack

สวัสดีเมื่อฉันกำลังโหลด WKWebview ด้วย Pinterest URL ฉันไม่สามารถเปิด "ดำเนินการต่อด้วย Facebook" ได้ ตอนนี้ฉันสามารถคลิกและรับข้อมูลเกี่ยวกับ "ต่อเนื่องกับ Google" ได้แล้ว ช่วยฉันด้วย
Nrv

ฉันกำลังประสบปัญหาเดียวกันกับ wkwebview; การเข้าสู่ระบบและการเปลี่ยนเส้นทางไม่ทำงานในแอปของฉัน ความช่วยเหลือใด ๆ
Jamshed Alam

1
โปรดทุกคนสามารถช่วยฉันได้ฉันได้มอบหมาย uiDelegate ตัวแทนให้กับ wkwebview แต่วิธีการสร้างมุมมองเว็บที่มีการกำหนดค่าไม่ได้ถูกเรียก
chetan panchal

25

เพิ่มตัวเองเป็นWKNavigationDelegate

_webView.navigationDelegate = self;

และใช้รหัสต่อไปนี้ในการตัดสินใจเรียกกลับของผู้รับมอบสิทธิ์การตัดสินใจนโยบายสำหรับการนำทาง: การตัดสินใจจัดการ:

- (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: รหัสนี้มาจากโครงการเล็ก ๆ ของฉันSTKWebKitViewControllerซึ่งรวม UI ที่ใช้งานได้รอบ ๆ WKWebView


1
มีการพิมพ์ผิด จุดหายไประหว่าง WKNavigationActionPolicy และ Allow
chicken

@stk จะเข้าใจได้อย่างไรว่า UIApplication จะเปิดลิงค์ในแอพ Safari ถ้าเป็นลิงค์ Appstore - ต้องเปิด ot ในแอพ Appstore แต่ถ้าเป็นหน้าเว็บปกติ - ต้องเปิดใน WKWebView stackoverflow.com/questions/29056854/…
BergP

1
@LeoKoppelkamm ไม่ใช่การพิมพ์ผิด แต่เป็น Objective-C ไม่ใช่ Swift ;)
Ayan Sengupta

1
ใช้งานได้กับลิงก์ แต่ดูเหมือนจะไม่พบหน้าต่างใหม่ที่เปิดด้วย JavaScript เช่นwindow.open(url, "_blank")
Crashalot

@Crashalot คุณพบวิธีแก้ปัญหาสำหรับ window.open หรือไม่?
สาม

14

หากคุณตั้งค่า WKWebView.navigationDelegate ไว้แล้ว

WKWebView.navigationDelegate = ตนเอง;

คุณต้องใช้:

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

ด้วยวิธีนี้คุณไม่จำเป็นต้องใช้เมธอด WKUIDelegate


1
วิธีนี้ใช้ได้กับลิงก์ แต่ดูเหมือนจะตรวจไม่พบหน้าต่างใหม่ที่เปิดด้วย JavaScript เช่น window.open (url, "_blank")
Crashalot

@Crashalot คุณตรวจคำตอบนี้แล้วหรือยัง? stackoverflow.com/questions/33190234/wkwebview-and-window-open
Jose Rego

8

วิธีแก้ปัญหาเหล่านั้นไม่ได้ผลสำหรับฉันฉันแก้ปัญหาโดย:

1) การใช้ WKUIDelegate

@interface ViewController () <WKNavigationDelegate, WKUIDelegate>

2) การตั้งค่า UIDelegate delegate ของ wkWebview

self.wkWebview.UIDelegate = self;

3) การใช้เมธอด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;  }

ฉันเพิ่งได้รับ SIGABRT เมื่อทำสิ่งนี้ มันใช้ไม่ได้
user2009449

8

Cloud xuคำตอบช่วยแก้ปัญหาของฉันได้

ในกรณีที่มีคนต้องการ Swift (4.x / 5.0) เวอร์ชันเทียบเท่านี่คือ:

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
}

แน่นอนคุณต้องตั้งค่าwebView.uiDelegateประการแรก


7

ฉันยืนยันว่ารหัส Swift ของ Bill Weinman ถูกต้อง แต่ต้องพูดถึงว่าคุณต้องมอบหมาย UIDelegate เพื่อให้มันใช้งานได้ในกรณีที่คุณยังใหม่กับ iOS ที่พัฒนาเหมือนฉัน

สิ่งนี้:

self.webView?.UIDelegate = self

มีสามสถานที่ที่คุณต้องทำการเปลี่ยนแปลง


พิมพ์รหัสของคุณคือ:self.webView.UIDelegate = self
Henrik Petterson

ถ้าไม่แน่ใจว่ามันส่อให้เห็น แต่ในการสั่งซื้อสำหรับสายนี้ของรหัสที่จะทำงานใน iOS 10 คุณต้องการที่จะสืบทอดViewController WKUIDelegateieclass ViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate
ltrainpr

4

คุณยังสามารถดันตัวควบคุมมุมมองอื่นหรือเปิดแท็บใหม่เป็นต้น:

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
}

จำเป็นต้องตั้งค่าvc.urlหรือไม่? ฉันไม่ได้ตั้งค่าและกำลังโหลดมุมมองเว็บอย่างถูกต้อง นอกจากนี้จากประสบการณ์ของฉันฉันเห็นcreateWebViewWithConfigurationว่าถูกเรียกเมื่อnavigationAction.targetFrameเป็นnilเท่านั้น คุณสามารถอธิบายสถานการณ์ที่ไม่เป็นจริงได้หรือไม่?
Mason G. Zhwiti

@ MasonG.Zhwiti ฉันใช้ WKWebViews สำหรับโปรเจ็กต์เมื่อฤดูใบไม้ร่วงที่ผ่านมา แต่หลังจากนั้นก็ยังไม่ได้ทำอะไรกับพวกเขา - ดังนั้นฉันจึงไม่สามารถตอบคำถามของคุณได้ ในเวลาที่ฉันโพสต์ข้างต้นมันได้ผล ฉันไม่สามารถเข้าใจวิธีการทำงานได้โดยไม่ต้องตั้งค่า vc.url
David H

ฉันคิดว่ามันใช้งานได้หากคุณส่งคืนมุมมองเว็บที่คุณสร้างขึ้นจากนั้น (ฉันคาดเดา) อะไรก็ตามที่ขอให้คุณสร้างมันจะดูแลโดยใช้มุมมองเว็บเพื่อโหลด URL ที่เป็นปัญหา
Mason G. Zhwiti

3

ขึ้นอยู่กับคำตอบของอัลเลนฮวง

รายละเอียด

  • Xcode เวอร์ชัน 10.3 (10G8), Swift 5

เป้าหมาย

  • ตรวจจับลิงก์ด้วย target=“_blank”
  • push ดูตัวควบคุมด้วย webView หากตัวควบคุมปัจจุบันมี navigationController
  • present ดูตัวควบคุมด้วย webView ในกรณีอื่น ๆ ทั้งหมด

สารละลาย

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

ตัวอย่างฉบับเต็ม

Info.plist

เพิ่มในการตั้งค่าความปลอดภัยการขนส่ง 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)
    }
}

สตอรี่บอร์ด

เวอร์ชัน 1

ใส่คำอธิบายภาพที่นี่

เวอร์ชัน 2

ใส่คำอธิบายภาพที่นี่


สวัสดีหลังจากเข้าสู่ระบบด้วย facebook แล้วจะเปิดหน้าต่างแชร์สำหรับแชร์ facebook ในลักษณะเดียวกันได้อย่างไร? ฉันลงชื่อเข้าใช้ด้วยการสร้าง wkwebview ใหม่ตอนนี้ผู้ใช้เข้าสู่ระบบแล้วตอนนี้จะติดตามหน้าต่างแชร์ได้อย่างไร
Jamshed Alam

ขอบคุณทางออกของคุณช่วยชีวิตฉัน :)
Samrat Pramanik

2

สิ่งนี้ใช้ได้ผลสำหรับฉัน:

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

อย่างที่คุณเห็นสิ่งที่เราทำที่นี่เป็นเพียงการเปิดwebViewurl ใหม่และควบคุมความเป็นไปได้ในการปิดหากคุณต้องการคำตอบจากที่second webviewจะแสดงในครั้งแรก


1

ฉันพบปัญหาบางอย่างที่ไม่สามารถแก้ไขได้ด้วยการใช้webView.load(navigationAction.request). ดังนั้นฉันจึงใช้สร้าง webView ใหม่เพื่อทำและมันก็ใช้ได้ดี

//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)) }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.