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

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

126516 次浏览

加上你自己作为 导航代表

_webView.navigationDelegate = self;

并在委托回调 DecidePolicyForNavigationAction: decision 处理程序:中实现以下代码

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

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

我的解决方案是取消导航并用 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;
}

你也可以推动另一个视图控制器,或者打开一个新的选项卡,等等:

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
}

来自@Cloud Xu 的答案是正确的:

// 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 的 Swift 密码是正确的。但是需要提到的是,如果你像我一样是 iOS 开发的新手,你还需要委托 UIDlegate 来完成它的工作。

就像这样:

self.webView?.UIDelegate = self

所以有三个地方你需要做出改变。

如果您已经设置了 WKWebView.NavationGenerate

导航代表 = 自我;

你只需要实现:

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

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

这些解决方案对我都不管用,我的确通过以下方式解决了这个问题:

1)实施 WKUIDlegate

@interface ViewController () <WKNavigationDelegate, WKUIDelegate>

2)设置 wkWebview 的 UIDlegate 委托

self.wkWebview.UIDelegate = self;

3)实施 创建 WebViewWithConfiguration方法

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

使用最新版本的 Swift 4.2 +

import WebKit

WKUID 代表扩展你的课程

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

我遇到了一些问题,不能解决只使用 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
}

基于 黄飞鸿回答

细节

  • Xcode Version 10.3(10G8) ,Swift 5

目标

  • 检测与 target=“_blank”的链路
  • 如果当前控制器具有 navigationController,则使用带有 webView 的 push视图控制器
  • 在所有其他情况下,使用带有 webView 的 present视图控制器

解决方案

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

enter image description here

版本2

enter image description here

这对我很有效:

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

正如您所看到的,我们在这里所做的只是用新的 URL 打开一个新的 webView,并控制关闭的可能性,只是如果您需要来自该 second webview的响应显示在第一个。

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

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

在 Mobile Safari 中打开空白页面,如果你使用 Swift:

webView.navigationDelegate = self

在 WK 导航中实现这一点。代表:

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if navigationAction.targetFrame == nil, let url = navigationAction.request.url {
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
decisionHandler(WKNavigationActionPolicy.allow)
}

使用此方法下载网页中的 pdf

Func webView (_ webView: WKWebView,createWebViewWith configuration: WKWebViewConfiguration,for navationAction: WKNavigationAction,windowFeatures: WKWindowFeatures)-> WKWebView?