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

IosWkwebview

Ios Problem Overview


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

Ios Solutions


Solution 1 - Ios

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.

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

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

Solution 2 - Ios

The answer from @Cloud Xu is the correct answer. Just for reference, here it is in 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
}

Solution 3 - Ios

To use latest version of Swift 4.2+

import WebKit

Extend your class with WKUIDelegate

Set delegate for 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
}

Solution 4 - Ios

Add yourself as the WKNavigationDelegate

_webView.navigationDelegate = self;

and implement following code in the delegate callback decidePolicyForNavigationAction: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.

Solution 5 - Ios

If you've already set the WKWebView.navigationDelegate

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.

Solution 6 - Ios

Cloud xu's answer solves my issue.

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

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.uiDelegate firstly.

Solution 7 - Ios

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

  1. Implementing WKUIDelegate

    @interface ViewController ()

  2. Setting the UIDelegate delegate of the wkWebview

    self.wkWebview.UIDelegate = self;

  3. Implementing the createWebViewWithConfiguration method

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

Solution 8 - Ios

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.

Something like this:

self.webView?.UIDelegate = self

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

Solution 9 - Ios

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
}

Solution 10 - Ios

Based on allen huang answer

Details

  • Xcode Version 10.3 (10G8), Swift 5

Targets

  • detect links with target=“_blank”
  • push view controller with webView if current controller has navigationController
  • present view controller with webView in all other cases

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

<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

enter image description here

Version 2

enter image description here

Solution 11 - Ios

This worked for me:

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

As you can see, what we do here is just opening a new webViewwith the new url and controlling the posibility of being closed, just if you need a response from that second webviewto be displayed on the first.

Solution 12 - Ios

To open _blank pages in Mobile Safari and if you use Swift:

webView.navigationDelegate = self

And implement this in the WKNavigationDelegate:

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

Solution 13 - Ios

I encountered some issues that can not be resolved by just using webView.load(navigationAction.request). So I use create a new webView to do and it just works fine.

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

Solution 14 - Ios

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

Solution 15 - Ios

Use this method to download pdf in web view

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionstkView Question on Stackoverflow
Solution 1 - IosCloud XuView Answer on Stackoverflow
Solution 2 - IosBill WeinmanView Answer on Stackoverflow
Solution 3 - IosmuhasturkView Answer on Stackoverflow
Solution 4 - IosstkView Answer on Stackoverflow
Solution 5 - IosBoris GeorgievView Answer on Stackoverflow
Solution 6 - IosBenjaminView Answer on Stackoverflow
Solution 7 - IosUgo MarinelliView Answer on Stackoverflow
Solution 8 - IosOliver ZhangView Answer on Stackoverflow
Solution 9 - IosDavid HView Answer on Stackoverflow
Solution 10 - IosVasily BodnarchukView Answer on Stackoverflow
Solution 11 - IosAitor PagánView Answer on Stackoverflow
Solution 12 - IosPau BalladaView Answer on Stackoverflow
Solution 13 - Iosallen huangView Answer on Stackoverflow
Solution 14 - Iosjayant rawatView Answer on Stackoverflow
Solution 15 - Iosshubham ramaniView Answer on Stackoverflow