Tại sao WKWebView không mở liên kết với target = “_ blank”?


122

WKWebViewkhông mở bất kỳ liên kết nào có target="_blank"thuộc tính 'Mở trong Cửa sổ mới' trong thẻ HTML <a href>-Tag của chúng.


vui lòng cập nhật câu trả lời đúng được đánh dấu
Efren

Câu trả lời:


184

Giải pháp của tôi là hủy điều hướng và tải lại yêu cầu bằng loadRequest:. Đây sẽ là hành vi tương tự như UIWebView luôn mở cửa sổ mới trong khung hiện tại.

Triển khai WKUIDelegateủy quyền và đặt nó thành _webview.uiDelegate. Sau đó thực hiện:

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

  return nil;
}

4
Đây là câu trả lời chính xác. Chúng tôi đã thử câu trả lời được chấp nhận nhưng không thành công. Nhưng câu trả lời của @ Cloud hoạt động và phản hồi này trên diễn đàn nhà phát triển giải thích tại sao.
Christopher Pickslay

5
Giải pháp này đã làm việc cho tôi. Đừng quên đặt thuộc UIDelegatetính, vì phương thức này được khai báo bằng WKUIDelegatekhông WKNavigationDelegate.
Taketo Sano

1
Được rồi, tôi thấy rằng mọi người muốn sử dụng WKWebView như thế này. Tôi đã triển khai khả năng mở target = _blank-Links trong cùng một WKWebView (như được hiển thị ở trên) trong dự án github.com/sticksen/STKWebKitViewController của tôi .
stk

5
@ChristopherPickslay khi sử dụng phương pháp này với WKWebView của tôi, url yêu cầu luôn trống, do đó, chế độ xem web hướng đến một trang trắng trống, có ý kiến ​​gì không?
Jason Murray

1
Khi yêu cầu '_blank' được gửi từ một biểu mẫu có dữ liệu bài đăng, tôi thấy dữ liệu bài đăng sẽ bị mất !!! Bất kỳ giúp đỡ? @Cloud Wu
Andrew

66

Câu trả lời từ @Cloud Xu là câu trả lời chính xác. Chỉ để tham khảo, đây là trong 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
bạn sẽ viết nó như thế nào nếu bạn muốn nó mở trong Safari? Ngoài ra, tôi cần tham khảo những gì trong bộ điều khiển chế độ xem để làm cho điều này hoạt động?
Jed Grant

1
Để mở trang mới trong mobile safari, hãy xem câu trả lời sau: stackoverflow.com/a/30604481/558789
Paul Bruneau

1
Điều này hoạt động đối với các liên kết, nhưng dường như không phát hiện ra các cửa sổ mới được mở bằng JavaScript, tức là window.open (url, "_blank")
Crashalot 28/10/16

FYI webView.loadRequest rõ ràng đã được đổi tên thành webView.load trong các phiên bản gần đây của API
Bill Weinman

52

Để sử dụng phiên bản mới nhất của Swift 4.2+

import WebKit

Mở rộng lớp học của bạn với WKUIDelegate

Đặt ủy quyền cho chế độ xem web

self.webView.uiDelegate = self

Triển khai phương pháp giao thức

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

Không phải là một bản sao thực sự. Có sự khác biệt về tính tùy chọn của các tham số không phù hợp với giao thức Swift 4.1 WKUIDelegate trong câu trả lời mà bạn đã liên kết đến.
yakattack

Xin chào, Khi tôi đang tải WKWebview với URL Pinterest, tôi không thể mở "Tiếp tục với Facebook". Bây giờ tôi có thể nhấp và nhận thông tin về "Tiếp tục với Google". Làm ơn giúp tôi với.
Nrv

Tôi đang đối mặt với cùng một vấn đề với wkwebview; đăng nhập và chuyển hướng không hoạt động trong ứng dụng của tôi. Bất kỳ trợ giúp?
Jamshed Alam

1
xin vui lòng bất cứ ai có thể giúp tôi ra, tôi đã giao đại biểu uiDelegate để wkwebview nhưng phương pháp của nó tạo xem web với cấu hình không được gọi là
PANCHAL chetan

25

Thêm chính bạn làm WKNavigationDelegate

_webView.navigationDelegate = self;

và triển khai mã sau trong lệnh gọi lại ủy quyền quyết địnhPolicyForNavigationAction: quyết địnhHandler:

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

Tái STKWebKitViewControllerbút : Mã này là từ dự án nhỏ của tôi , bao bọc một giao diện người dùng có thể sử dụng xung quanh WKWebView.


1
Có một lỗi đánh máy. Thiếu một dấu chấm giữa WKNavigationActionPolicy và Allow

@stk Làm cách nào để hiểu được liệu UIApplication sẽ mở liên kết trong ứng dụng Safari? Nếu đó là liên kết Appstore - tôi cần mở ot trong ứng dụng Appstore, nhưng nếu đó là trang web thông thường - tôi cần mở nó trong cùng một WKWebView stackoverflow.com/questions/29056854/…
BergP

1
@LeoKoppelkamm Đây không phải lỗi đánh máy mà là Objective-C không phải Swift. ;)
Ayan Sengupta

1
Công trình này cho các liên kết, nhưng dường như không phát hiện cửa sổ mới mở ra với JavaScript, tức là,window.open(url, "_blank")
Crashalot

@Crashalot Bạn đã tìm thấy giải pháp cho window.open chưa?
Sam

14

Nếu bạn đã đặt WKWebView.navigationDelegate

WKWebView.navigationDelegate = bản thân;

bạn chỉ cần thực hiện:

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

theo cách này, bạn không cần triển khai phương thức WKUIDelegate.


1
Điều này hoạt động đối với các liên kết, nhưng dường như không phát hiện ra các cửa sổ mới được mở bằng JavaScript, tức là window.open (url, "_blank")
Crashalot 28/10/16

@Crashalot Bạn đã kiểm tra câu trả lời này chưa? stackoverflow.com/questions/33190234/wkwebview-and-window-open
Jose Rego

8

Không có giải pháp nào trong số đó hiệu quả với tôi, tôi đã giải quyết vấn đề bằng cách:

1) Triển khai WKUIDelegate

@interface ViewController () <WKNavigationDelegate, WKUIDelegate>

2) Đặt đại biểu UIDelegate của wkWebview

self.wkWebview.UIDelegate = self;

3) Triển khai phương thức 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;  }

Tôi chỉ nhận được một SIGABRT khi thực hiện việc này. Nó không hoạt động.
user2009449

8

Cloud xuCâu trả lời giải quyết vấn đề của tôi.

Trong trường hợp ai đó cần phiên bản Swift (4.x / 5.0) tương đương, đây là nó:

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
}

Tất nhiên bạn phải đặt webView.uiDelegatetrước.


7

Tôi xác nhận rằng mã Swift của Bill Weinman là chính xác. Nhưng cần đề cập rằng bạn cũng cần ủy quyền cho UIDelegate để nó hoạt động, trong trường hợp bạn mới phát triển iOS như tôi.

Một cái gì đó như thế này:

self.webView?.UIDelegate = self

Vì vậy, có ba điểm mà bạn cần thực hiện thay đổi.


Nhập mã của bạn vào, đó là:self.webView.UIDelegate = self
Henrik Petterson

Không chắc liệu nó có ngụ ý hay không, nhưng để dòng mã này hoạt động trong iOS 10, bạn ViewControllercần kế thừa WKUIDelegate. tức làclass ViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate
ltrainpr

4

Bạn cũng có thể đẩy một bộ điều khiển chế độ xem khác hoặc mở một tab mới, v.v.:

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
}

Có cần thiết phải đặt vc.urlkhông? Tôi không đặt nó và chế độ xem web đang được tải đúng cách. Ngoài ra, theo kinh nghiệm của tôi, tôi chỉ thấy createWebViewWithConfigurationđược gọi khi navigationAction.targetFrameđược nil. Bạn có thể mô tả một kịch bản mà điều này sẽ không đúng không?
Mason G. Zhwiti

@ MasonG.Zhwiti Tôi đã tích cực sử dụng WKWebViews cho một dự án vào mùa thu năm ngoái, nhưng kể từ đó không làm gì với chúng - vì vậy tôi thực sự không thể trả lời câu hỏi của bạn. Tại thời điểm tôi đăng ở trên, nó đã hoạt động. Tuy nhiên, tôi không thể hiểu nó hoạt động như thế nào nếu không thiết lập vc.url.
David H

Tôi nghĩ rằng nó hoạt động mà không phải vì bạn đang trả lại chế độ xem web mà bạn đã tạo và sau đó (tôi đoán) bất cứ thứ gì yêu cầu bạn tạo nó sẽ xử lý việc sử dụng chế độ xem web để tải URL được đề cập.
Mason G. Zhwiti

3

Dựa trên câu trả lời của allen huang

Chi tiết

  • Phiên bản Xcode 10.3 (10G8), Swift 5

Mục tiêu

  • phát hiện các liên kết với target=“_blank”
  • push xem bộ điều khiển với webView nếu bộ điều khiển hiện tại có navigationController
  • present xem bộ điều khiển với webView trong tất cả các trường hợp khác

Giải pháp

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

Đầy đủ mẫu

Info.plist

thêm vào cài đặt bảo mật truyền tải Info.plist của bạn

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

Bảng phân cảnh

Phiên bản 1

nhập mô tả hình ảnh ở đây

Phiên bản 2

nhập mô tả hình ảnh ở đây


Chào bạn, sau khi đăng nhập bằng facebook thì cách mở cửa sổ share cho facebook share tương tự như thế nào? Tôi đã đăng nhập bằng cách tạo wkwebview mới, bây giờ người dùng đã đăng nhập. Bây giờ làm cách nào để theo dõi cửa sổ chia sẻ?
Jamshed Alam

Cảm ơn bạn, giải pháp của bạn tiết kiệm cuộc sống của tôi :)
Samrat Pramanik

2

Điều này đã làm việc cho tôi:

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

Như bạn có thể thấy, những gì chúng tôi làm ở đây chỉ là mở một webViewurl mới và kiểm soát khả năng bị đóng, chỉ cần bạn cần phản hồi từ đó second webviewsẽ được hiển thị ở lần đầu tiên.


1

Tôi gặp phải một số vấn đề không thể giải quyết bằng cách sử dụng webView.load(navigationAction.request). Vì vậy, tôi sử dụng tạo một webView mới để làm và nó hoạt động tốt.

//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)) }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.