Почему WKWebView не открывает ссылки с target = «_ blank»?

122

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

клейкий
источник
обновите, пожалуйста, правильный ответ, помеченный
Эфрен

Ответы:

184

Мое решение - отменить навигацию и снова загрузить запрос с помощью 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;
}
Облако Сюй
источник
4
Это правильный ответ. Мы безуспешно попробовали принятый ответ. Но ответ @ Cloud работает, и этот ответ на форумах разработчиков объясняет, почему.
Кристофер Пикслей,
5
Это решение сработало для меня. Не забудьте установить UIDelegateсвойство, поскольку эти методы объявлены в WKUIDelegatenot 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
К вашему сведению, 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
}
muhasturk
источник
Не настоящий дубликат. Существуют различия в необязательности параметров, которые не соответствуют протоколу Swift 4.1 WKUIDelegate в ответе, на который вы ссылаетесь.
yakattack 03
Привет! Когда я загружал WKWebview с URL-адресом Pinterest, мне не удалось открыть «Продолжить с Facebook». Теперь я могу щелкнуть и получить информацию о «Продолжить с Google». Помогите мне, пожалуйста.
Nrv
У меня такая же проблема с wkwebview; логин и перенаправление не работали в моем приложении. Любая помощь ?
Джамшед Алам
1
пожалуйста, кто-нибудь может мне помочь, я назначил делегата uiDelegate на wkwebview, но его метод создания веб-представления с конфигурацией не
вызывается
25

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

_webView.navigationDelegate = self;

и реализуйте следующий код в обратном вызове делегата resolvePolicyForNavigationAction: 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.

клейкий
источник
1
Опечатка. Между WKNavigationActionPolicy и Allow отсутствует точка
цыпленок
@stk Как я могу понять, собирается ли UIApplication открывать ссылку в приложении Safari? Если это ссылка на Appstore - мне нужно открыть ot в приложении Appstore, но если это обычная веб-страница - мне нужно открыть ее в том же WKWebView stackoverflow.com/questions/29056854/…
BergP
1
@LeoKoppelkamm Это не опечатка, это скорее Objective-C, а не Swift. ;)
Аян Сенгупта
1
Это работает для ссылок, но, похоже, не обнаруживает новые окна, открытые с помощью JavaScript, т. window.open(url, "_blank")
Е.
@Crashalot Вы нашли решение для window.open?
Сэм
14

Если вы уже установили 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.

Борис Георгиев
источник
1
Это работает для ссылок, но, похоже, не обнаруживает новые окна, открытые с помощью JavaScript, например window.open (url, «_blank»)
Crashalot
@Crashalot Вы проверили этот ответ? stackoverflow.com/questions/33190234/wkwebview-and-window-open
Jose Rego
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 при этом. Не работает.
user2009449
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

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

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

self.webView?.UIDelegate = self

Итак, есть три места, где вам нужно внести изменения.

Оливер Чжан
источник
Введите свой код, это:self.webView.UIDelegate = self
Хенрик Петтерсон,
Не уверен, подразумевается ли это, но для того, чтобы эта строка кода работала в iOS 10, вам ViewControllerнеобходимо наследовать WKUIDelegate. ieclass 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 во всех остальных случаях

Решение

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 с новым URL и контролируем возможность закрытия, если вам нужен ответ от него, который second webviewбудет отображаться при первом.

Айтор Паган
источник
1

Я столкнулся с некоторыми проблемами, которые нельзя решить, просто используя webView.load(navigationAction.request) . Поэтому я использую create a new 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
}
Аллен Хуанг
источник
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)) }
}
Jayant Rawat
источник