ios 为什么 WKWebView 不打开带有 target="_blank" 的链接?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25713069/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 02:16:03  来源:igfitidea点击:

Why is WKWebView not opening links with target="_blank"?

ioswkwebview

提问by stk

WKWebViewdoes not open any links which have target="_blank"a.k.a. 'Open in new Window' attribute in their HTML <a href>-Tag.

WKWebView不会打开任何target="_blank"在其 HTML<a href>标签中具有“在新窗口中打开”属性的链接。

回答by Cloud Xu

My solution is to cancel the navigation and load the request with loadRequest: again. This will be come the similar behavior like UIWebView which always open new window in the current frame.

我的解决方案是取消导航并再次使用 loadRequest: 加载请求。这将是类似 UIWebView 的行为,它总是在当前帧中打开新窗口。

Implement the WKUIDelegatedelegate and set it to _webview.uiDelegate. Then implement:

实现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;
}

回答by Bill Weinman

The answer from @Cloud Xu is the correct answer. Just for reference, here it is in Swift:

来自@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
}

回答by muhasturk

To use latest version of Swift 4.2+

使用最新版本的 Swift 4.2+

import WebKit

Extend your class with WKUIDelegate

使用WKUIDelegate扩展您的课程

Set delegate for webview

为 webview 设置委托

self.webView.uiDelegate = self

Implement protocol method

实现协议方法

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

回答by stk

Add yourself as the WKNavigationDelegate

将自己添加为WKNavigationDelegate

_webView.navigationDelegate = self;

and implement following code in the delegate callback decidePolicyForNavigationAction:decisionHandler:

并在委托回调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);
}

P.S.: This code is from my little project STKWebKitViewController, which wraps a usable UI around WKWebView.

PS:这段代码来自我的小项目STKWebKitViewController,它围绕 WKWebView 包装了一个可用的 UI。

回答by Boris Georgiev

If you've already set the WKWebView.navigationDelegate

如果您已经设置了 WKWebView.navigationDelegate

WKWebView.navigationDelegate = self;

WKWebView.navigationDelegate = self;

you just need to implement:

你只需要实现:

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

this way you don't need to implement the WKUIDelegate method.

这样您就不需要实现 WKUIDelegate 方法。

回答by Ugo Marinelli

None of those solutions worked for me, I did solve the issue by :

这些解决方案都不适合我,我确实通过以下方式解决了问题:

1) Implementing WKUIDelegate

1) 实现 WKUIDelegate

@interface ViewController () <WKNavigationDelegate, WKUIDelegate>

2) Setting the UIDelegate delegate of the wkWebview

2)设置wkWebview的UIDelegate委托

self.wkWebview.UIDelegate = self;

3) Implementing the createWebViewWithConfigurationmethod

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

回答by Oliver Zhang

I confirm that Bill Weinman's Swift code is correct. But need to mention that you also need to delegate UIDelegate for it to work, in case you are new to iOS developing like me.

我确认 Bill Weinman 的 Swift 代码是正确的。但需要提及的是,您还需要委托 UIDelegate 才能使其工作,以防您像我一样是 iOS 开发新手。

Something like this:

像这样的东西:

self.webView?.UIDelegate = self

So there's three places that you need to make changes.

因此,您需要在三个地方进行更改。

回答by David H

You can also push another view controller, or open a new tab, 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
}

回答by 0xa6a

Cloud xu's answer solves my issue.

Cloud xu的答案解决了我的问题。

In case someone need the equivalent Swift(4.x/5.0) version, here is it:

如果有人需要等效的 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
}

Of course you have to set webView.uiDelegatefirstly.

当然你webView.uiDelegate要先设置。

回答by Vasily Bodnarchuk

Based on allen huang answer

基于Allen huang 的回答

Details

细节

  • Xcode Version 10.3 (10G8), Swift 5
  • Xcode 版本 10.3 (10G8),Swift 5

Targets

目标

  • detect links with target=“_blank”
  • pushview controller with webView if current controller has navigationController
  • presentview controller with webView in all other cases
  • 检测链接 target=“_blank”
  • push如果当前控制器有,则带有 webView 的视图控制器 navigationController
  • present在所有其他情况下带有 webView 的视图控制器

Solution

解决方案

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

Full sample

完整样品

Info.plist

信息表

add in your Info.plist transport security setting

添加您的 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

故事板

Version 1

版本 1

enter image description here

在此处输入图片说明

Version 2

版本 2

enter image description here

在此处输入图片说明