Edited at

WKWebViewでtarget="_blank"なリンクが開かない時の対処法

More than 1 year has passed since last update.

普通にWKWebViewを実装したらtarget="_blank"なリンクが押されたときに無反応になるので対処してみました。別の回避方法がありましたらコメント欄にて教えて下さい。

ついでにアプリリンク(iTunes)や他アプリへのURL Schemeの対応も書いています。

(※ WKNavigationDelegateプロトコルとwebView.navigationDelegate = selfとすることをお忘れなく。)


Swift 3.0版

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {

guard let url = navigationAction.request.url else {
decisionHandler(.cancel)
return
}

if url.absoluteString.range(of: "//itunes.apple.com/") != nil {
if UIApplication.shared.responds(to: #selector(UIApplication.open(_:options:completionHandler:))) {
UIApplication.shared.open(url, options: [UIApplicationOpenURLOptionUniversalLinksOnly:false], completionHandler: { (finished: Bool) in
})
}
else {
// iOS 10 で deprecated 必要なら以降のopenURLも振り分ける
// iOS 10以降は UIApplication.shared.open(url, options: [:], completionHandler: nil)
UIApplication.shared.openURL(url)
}
decisionHandler(.cancel)
return
}
else if !url.absoluteString.hasPrefix("http://")
&& !url.absoluteString.hasPrefix("https://") {
// URL Schemeをinfo.plistで公開しているアプリか確認
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.openURL(url)
decisionHandler(.cancel)
return
}
// // 確認せずとりあえず開く
// UIApplication.shared.openURL(url)
// decisionHandler(.cancel)
// return
}

switch navigationAction.navigationType {
case .linkActivated:
if navigationAction.targetFrame == nil
|| !navigationAction.targetFrame!.isMainFrame {
// <a href="..." target="_blank"> が押されたとき
webView.load(URLRequest(url: url))
decisionHandler(.cancel)
return
}
case .backForward:
break
case .formResubmitted:
break
case .formSubmitted:
break
case .other:
break
case .reload:
break
} // 全要素列挙した場合はdefault不要 (足りない要素が追加されたときにエラーを吐かせる目的)

decisionHandler(.allow)
}


別法

コメント欄に @taketo1024 さんが書いてくださった方法のSwift 3.0版です。

(※ 上記はプロトコルWKNavigationDelegatewebView.navigationDelegate = selfを指定することで動きますが、こちらはWKUIDelegatewebView.uiDelegate = selfと指定します。)

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

guard let url = navigationAction.request.url else {
return nil
}

guard let targetFrame = navigationAction.targetFrame, targetFrame.isMainFrame else {
webView.load(URLRequest(url: url))
return nil
}
return nil
}


Objective-C

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler

{
NSURL *url = navigationAction.request.URL;

// iTunes: アプリのリンクなど
if ([url.absoluteString rangeOfString:@"//itunes.apple.com/"].location != NSNotFound) {
[[UIApplication sharedApplication] openURL:url];
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
// http(s) 以外のプロトコル: 他のアプリへ移動する場合
else if (![url.absoluteString hasPrefix:@"http://"]
&& [url.absoluteString hasPrefix:@"https://"]) {
// URL Schemeをinfo.plistで公開しているかを確認
// if ([[UIApplication sharedApplication] canOpenURL:url]]) {
// [[UIApplication sharedApplication] openURL:url];
// decisionHandler(WKNavigationActionPolicyCancel);
// }
// or 確認せず開く
[[UIApplication sharedApplication] openURL:url];
decisionHandler(WKNavigationActionPolicyCancel);
return;
}

switch (navigationAction.navigationType) {
case WKNavigationTypeLinkActivated: {
NSLog(@"WKNavigationTypeLinkActivated::");
// <a href="..." target="_blank"> が押されたとき
if (!navigationAction.targetFrame
|| !navigationAction.targetFrame.isMainFrame) {
[webView loadRequest:[NSURLRequest requestWithURL:url]];
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
break;
}
case WKNavigationTypeFormSubmitted: {
NSLog(@"WKNavigationTypeFormSubmitted::");
break;
}
case WKNavigationTypeBackForward: {
NSLog(@"WKNavigationTypeBackForward::");
break;
}
case WKNavigationTypeReload: {
NSLog(@"WKNavigationTypeReload::");
break;
}
case WKNavigationTypeFormResubmitted: {
NSLog(@"WKNavigationTypeFormResubmitted::");
break;
}
case WKNavigationTypeOther: {
NSLog(@"WKNavigationTypeOther::");
break;
}
} // 全要素列挙した場合はdefault不要 (足りない要素が追加されたときにエラーを吐かせる目的)

decisionHandler(WKNavigationActionPolicyAllow);
}