Почему WKWebView не открывает ссылки с target="_blank"?

WKWebView не открывает ссылки, которые имеют target="_blank" aka атрибут "Открыть в новом окне" в своем HTML <a href>-Тег.

15 ответов

Решение

Мое решение - отменить навигацию и снова загрузить запрос с помощью 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;
}

Ответ от @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
}

Чтобы использовать последнюю версию Swift 4.2+

import WebKit

Расширьте свой класс с помощью WKUIDelegate

Установить делегата для веб-просмотра

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
}

Добавьте себя в качестве WKNavigationDelegate

_webView.navigationDelegate = self;

и реализовать следующий код в обратном вызове делегатаmanagePolicyForNavigationAction: SolutionHandler:

- (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, который оборачивает полезный пользовательский интерфейс вокруг WKWebView.

Если вы уже установили WKWebView.navigationDelegate

WKWebView.navigationDelegate = self;

вам просто нужно реализовать:

- (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.

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 в первую очередь.

Ни одно из этих решений не помогло мне, я решил проблему:

1) Реализация WKUIDelegate

@interface ViewController () <WKNavigationDelegate, WKUIDelegate>

2) Настройка UIDelegate делегата 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;  }

Я подтверждаю, что код Swift Билла Вайнмана правильный. Но нужно отметить, что вам также нужно делегировать UIDelegate, чтобы он работал, если вы новичок в iOS, развивающемся, как я.

Что-то вроде этого:

self.webView?.UIDelegate = self

Таким образом, есть три места, в которые нужно внести изменения.

Вы также можете нажать другой контроллер представления или открыть новую вкладку и т.д.:

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
}

Решение

Аллен Хуанг ответ

подробности

xCode 9.2, Swift 4

Полный пример с нажатием ссылки target = "_ blank"

import UIKit
import WebKit

class ViewController: UIViewController {

    var urlString = "http://google.com"
    var webView: WKWebView!
    fileprivate var webViewIsInited = false
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func viewWillLayoutSubviews() {
        if !webViewIsInited {
            webViewIsInited = true
            if webView == nil {
                webView = WKWebView(frame: UIScreen.main.bounds, configuration: WKWebViewConfiguration())
            }

            view.addSubview(webView)
            webView.navigationDelegate = self
            webView.uiDelegate = self
            webView.loadUrl(string: urlString)
        }
    }
}

extension ViewController: WKNavigationDelegate {

    func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
        decisionHandler(.allow)
    }

    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        if let host = webView.url?.host {
            self.navigationItem.title = host
        }
    }
}

extension ViewController: WKUIDelegate {

    func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
        if navigationAction.targetFrame == nil {
            let vc = ViewController()
            vc.urlString = navigationAction.request.url?.absoluteString ?? "http://google.com"
            vc.view.frame = UIScreen.main.bounds
            vc.webView = WKWebView(frame: UIScreen.main.bounds, configuration: configuration)
            navigationController?.pushViewController(vc, animated: false)
            return vc.webView
        }
        return nil
    }
}

extension WKWebView {

    func loadUrl(string: String) {
        if let url = URL(string: string) {
            if self.url?.host == url.host {
                self.reload()
            } else {
                load(URLRequest(url: url))
            }
        }
    }
}

Чтобы открывать пустые страницы в Mobile Safari и если вы используете Swift:

      webView.navigationDelegate = self

И реализуем это в 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)
}

Это сработало для меня:

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

Как вы видите, то, что мы делаем здесь, это просто открытие нового webViewс новым URL-адресом и контролем возможности закрытия, просто если вам нужен ответ от этого 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)) }
}

Я столкнулся с некоторыми проблемами, которые не могут быть решены, просто используя 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
}

Используйте этот метод для загрузки PDF в веб-просмотре

func webView (_ webView: WKWebView, createWebViewWith конфигурация: WKWebViewConfiguration, для navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView?

Другие вопросы по тегам