Чому WKWebView не відкриває посилання з target = "_ blank"?


122

WKWebViewне відкриває жодних посилань, які мають target="_blank"атрибут aka "Відкрити в новому вікні" у своєму HTML- <a href>Тезі.


будь ласка, оновіть правильну відповідь із позначкою
Ефрен

Відповіді:


184

Моє рішення - скасувати навігацію та завантажити запит loadload (знову). Це буде подібна поведінка, як 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;
}

4
Це правильна відповідь. Ми спробували прийняту відповідь без успіху. Але відповідь @ Cloud працює, і ця відповідь на форумах розробників пояснює, чому.
Крістофер Пікслі

5
Це рішення спрацювало на мене. Не забудьте встановити UIDelegateвластивість, оскільки цей метод оголошено WKUIDelegateне WKNavigationDelegate.
Taketo Sano

1
Гаразд, я бачу, що люди хочуть використовувати WKWebView так. Я реалізував можливість відкрити target = _blank-Links у тому ж WKWebView (як показано вище) у своєму проекті github.com/sticksen/STKWebKitViewController .
stk

5
@ChristopherPickslay при використанні цього методу з моїм WKWebView URL-адреса запиту завжди порожня, тому веб-перегляд спрямовує на порожню білу сторінку, будь-які ідеї?
Джейсон Мюррей

1
Коли запит '_blank' надсилається з форми з даними про публікацію, я знаходжу, що дані про публікацію будуть втрачені !!! Будь-яка допомога? @Cloud Wu
Ендрю

66

Відповідь від @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
}

2
як би ви написали це, якби хотіли, щоб він відкрився в Safari замість цього? Крім того, що мені потрібно вказати в контролері перегляду, щоб це працювало?
Джед Грант

1
Щоб відкрити нову сторінку в мобільному сафарі, дивіться цю відповідь: stackoverflow.com/a/30604481/558789
Пол Бруно

1
Це працює для посилань, але, схоже, не виявляє нових вікон, відкритих за допомогою JavaScript, тобто window.open (url, "_blank")
Crashalot

FYI webView.loadRequest, очевидно, було перейменовано на webView.load в останніх версіях API
Білл Вайнман

52

Щоб використовувати останню версію 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
}

Не справжній дублікат. Існують відмінності у необов'язковості параметрів, які не відповідають протоколу Swift 4.1 WKUIDelegate у відповіді, до якої ви зв'язані.
якукат напад

Привіт, Коли я завантажую WKWebview за допомогою URL-адреси Pinterest, мені не вдалося відкрити "Продовжити з Facebook". Тепер я можу натиснути та отримати інформацію на тему "Продовжити з Google". Допоможіть мені, будь ласка.
Nrv

Я зіткнувся з тим же питанням і з wkwebview; логін та переадресація не працюють у моєму додатку. Будь-яка допомога?
Джамшед Алам

1
будь-хто може допомогти мені, я призначив делегата uiDelegate на wkwebview, але його метод створення веб-перегляду з конфігурацією не називається
chetan panchal

25

Додайте себе як WKNavigationDelegate

_webView.navigationDelegate = self;

та реалізувати наступний код у зворотному делегатному делегаті рішенняPolicyForNavigationAction: 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);
}

PS: Цей код від мого маленького проекту STKWebKitViewController, який обертає корисний інтерфейс навколо WKWebView.


1
Там друкарська помилка. Немає крапки між WKNavigationActionPolicy і Allow
курка

@stk Як я можу зрозуміти, чи буде UIApplication відкривати посилання в додатку Safari? Якщо це Appstore посилання - мені потрібно , щоб відкрити ВЗ в додатку Appstore, але якщо це звичайна веб - сторінка - Мені потрібно , щоб відкрити його в тому ж WKWebView stackoverflow.com/questions/29056854 / ...
BergP

1
@LeoKoppelkamm Це не помилка друку, це це Objective-C, а не Swift. ;)
Аян Сенгупта

1
Це працює для посилань, але, схоже, не виявляє нових вікон, відкритих за допомогою JavaScript, тобтоwindow.open(url, "_blank")
Crashalot

@Crashalot Ви знайшли рішення для window.open?
Сем

14

Якщо ви вже встановили WKWebView.navigationDelegate

WKWebView.navigationDelegate = Я;

вам просто потрібно здійснити:

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


1
Це працює для посилань, але, схоже, не виявляє нових вікон, відкритих за допомогою JavaScript, тобто window.open (url, "_blank")
Crashalot

@Crashalot Ви перевірили цю відповідь? stackoverflow.com/questions/33190234/wkwebview-and-window-open
Хосе Рего

8

Жодне з цих рішень не працювало для мене, я вирішив це питання:

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

Я просто отримую SIGABRT, роблячи це. Це не працює.
користувач2009449

8

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спочатку.


7

Я підтверджую, що код Свіфта Білла Вайнмана правильний. Але потрібно зазначити, що вам також потрібно делегувати UIDelegate, щоб він працював, якщо ви новачок в iOS розвивається, як я.

Щось на зразок цього:

self.webView?.UIDelegate = self

Отже, потрібно змінити три місця.


Введіть свій код, це:self.webView.UIDelegate = self
Генрік Петтерсон

Не впевнений, чи це мається на увазі, але для того, щоб цей рядок коду працював в iOS 10, вам ViewControllerпотрібно успадкувати WKUIDelegate. тобтоclass ViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate
ltrainpr

4

Ви також можете натиснути інший контролер перегляду або відкрити нову вкладку тощо:

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
}

Чи потрібно встановлювати vc.url? Я не встановлюю це, і веб-перегляд завантажується належним чином. Крім того, на моєму досвіді я бачу лише createWebViewWithConfigurationпокликаний, коли navigationAction.targetFrameє nil. Чи можете ви описати сценарій, коли це не було би правдою?
Мейсон Жвіті

@ MasonG.Zhwiti Я активно використовував WKWebViews для проекту минулої осені, але з тих пір нічого не робив з ними - тому я справді не можу відповісти на ваше запитання. У той час, коли я публікував вище, це спрацювало. Я не можу зрозуміти, як це працює, не встановлюючи vc.url.
Девід Н

Я думаю, що це працює без того, що ви повертаєте створений вами веб-перегляд, а потім (я здогадуюсь) те, що вас попросило створити, піклується про використання веб-перегляду для завантаження відповідної URL-адреси.
Мейсон Жвіті

3

На основі відповіді Аллена Хуанга

Деталі

  • Версія Xcode 10.3 (10G8), Swift 5

Цілі

  • виявити зв’язки з target=“_blank”
  • push переглянути контролер з webView, якщо поточний контролер є navigationController
  • 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

введіть тут опис зображення

Версія 2

введіть тут опис зображення


привіт, після входу в facebook, як відкрити вікно для поділу facebook подібно подібним чином? Я ввійшов у систему, створивши новий wkwebview, тепер користувач увійшов. Тепер як відстежувати вікно спільного доступу?
Джамшед Алам

Дякую, ваше рішення врятує моє життя :)
Самрат Праманик

2

Це працювало для мене:

-(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з’явиться на першому.


1

У мене виникли деякі проблеми, які неможливо вирішити просто використанням webView.load(navigationAction.request). Тому я використовую створити новий веб-перегляд, і це просто чудово працює.

//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)) }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.