ios - type - ¿Por qué WKWebView no abre enlaces con target="_ blank"?
wkwebview before ios 11.0(nscoding support was broken in previous versions) (11)
WKWebView
no abre ningún enlace que tenga el atributo "Abrir en una nueva ventana" de target="_blank"
en su HTML <a href>
-Tag.
Solución
allen huang contesta
Detalles
Xcode 9.2, Swift 4
Muestra completa con enlaces de 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))
}
}
}
}
Agregue usted mismo como WKNavigationDelegate
_webView.navigationDelegate = self;
e implemente el siguiente código en la devolución de llamada delegada 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);
}
PD: Este código es de mi pequeño proyecto github.com/sticksen/STKWebKitViewController , que envuelve una interfaz de usuario utilizable alrededor de WKWebView.
Confirmo que el código Swift de Bill Weinman es correcto. Pero debo mencionar que también debe delegar UIDelegate para que funcione, en caso de que sea nuevo en el desarrollo de iOS como yo.
Algo como esto:
self.webView?.UIDelegate = self
Entonces hay tres lugares en los que necesitas hacer cambios.
Encontré algunos problemas que no se pueden resolver utilizando simplemente webView.load(navigationAction.request)
. Así que uso crear una nueva vista web para hacer y simplemente funciona bien.
//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
}
Esto funcionó para mí:
-(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;
}
Como puede ver, lo que hacemos aquí es simplemente abrir una nueva vista webView
con la nueva URL y controlar la posibilidad de que se cierre, solo si necesita una respuesta de esa second webview
para que se muestre en la primera.
La respuesta de @Cloud Xu es la respuesta correcta. Sólo para referencia, aquí está en 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
}
Mi solución es cancelar la navegación y cargar la solicitud con loadRequest: nuevamente. Este será el comportamiento similar a UIWebView, que siempre abre una nueva ventana en el marco actual.
Primero necesitas tener una implementación de WKUIDelegate
. Y _webview.UIDelegate
en _webview.UIDelegate
. Luego implementa:
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
{
if (!navigationAction.targetFrame.isMainFrame) {
[webView loadRequest:navigationAction.request];
}
return nil;
}
Ninguna de esas soluciones funcionó para mí, resolví el problema por:
1) Implementando WKUIDelegate
@interface ViewController () <WKNavigationDelegate, WKUIDelegate>
2) Configuración del delegado UIDelegate de wkWebview
self.wkWebview.UIDelegate = self;
3) Implementando el método 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; }
Para usar la última versión de Swift 4.2+
import WebKit
Extiende tu clase con WKUIDelegate
Establecer delegado para webview
self.webView.uiDelegate = self
Implementar el método de protocolo.
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
if navigationAction.targetFrame == nil {
webView.load(navigationAction.request)
}
return nil
}
Si ya ha configurado el WKWebView.navigationDelegate
WKWebView.navigationDelegate = self;
solo necesitas implementar:
- (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);
}
de esta manera no es necesario implementar el método WKUIDelegate.
También puede presionar otro controlador de vista, o abrir una nueva pestaña, 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
}