在Safari中打开WKWebview中的target="_blank"链接

12

我正在尝试让我的混合IOS应用程序,它使用Swift和WKWebviews,打开一个在Mobile Safari中具有target="_blank"的链接,或者如果URL包含http://https://mailto:

这个答案中,我得到了这段代码。

func webView(webView: WKWebView!, createWebViewWithConfiguration     configuration: WKWebViewConfiguration!, forNavigationAction navigationAction:     WKNavigationAction!, windowFeatures: WKWindowFeatures!) -> WKWebView! {
    if navigationAction.targetFrame == nil {
        webView.loadRequest(navigationAction.request)
    }
    return nil
}

首先,那对我没有任何作用。其次,我希望它在新窗口中打开。我找到了这段代码,它应该能够做到类似的事情...

if let requestUrl = NSURL(string: "http://www.iSecurityPlus.com") {
     UIApplication.sharedApplication().openURL(requestUrl)
}

我该如何将这两者结合起来使它们能够工作?我需要在ViewController声明中添加什么才能使其正常工作?

6个回答

13

在(从这里)

 override func loadView() {
    super.loadView()
    self.webView.navigationDelegate = self 
    self.webView.UIDelegate = self  //must have this
 }

然后添加该函数(从这里开始,并进行补充)...

func webView(webView: WKWebView,
    createWebViewWithConfiguration configuration: WKWebViewConfiguration,
    forNavigationAction navigationAction: WKNavigationAction,
    windowFeatures: WKWindowFeatures) -> WKWebView? {
        if navigationAction.targetFrame == nil {
            var url = navigationAction.request.URL
            if url.description.lowercaseString.rangeOfString("http://") != nil || url.description.lowercaseString.rangeOfString("https://") != nil || url.description.lowercaseString.rangeOfString("mailto:") != nil  {
                UIApplication.sharedApplication().openURL(url)
            }
        }
        return nil
}

不确定是否已经明确,但是为了使 self.webView.UIDelegate = self 在 iOS 10 中起作用,你的 ViewController 需要继承 WKUIDelegate。即 class ViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate - ltrainpr
我已经添加了这段代码并更新了它以适应iOS 10和Swift 3(请参见我的答案中转换后的代码)。某种原因导致createWebViewWith configuration: WKWebViewConfiguration没有被调用。我没有loadView函数,但我已经在viewDidLoad函数中添加了self.webView.uiDelegate = self。我错过了什么? - ltrainpr
这似乎对使用JavaScript打开的链接无效,即window.open(url, "_blank")。你试过了吗? - Crashalot
3
我上个月刚用过它,现在它不再工作了。 - KarenAnne

13

首先添加 WKNavigationDelegate,然后将 webviewWk.navigationDelegate = self

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: (WKNavigationActionPolicy) -> Void) {
            
// 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.navigationType == WKNavigationType.linkActivated {
           if let url = navigationAction.request.url, UIApplication.shared.canOpenURL(url) {
               print("Trying to open \(url.absoluteString)")
               UIApplication.shared.open(url)
            }
       }
       decisionHandler(WKNavigationActionPolicy.allow)
}

只是出于好奇,let urlString = url!.absoluteString 这段代码是做什么用的?变量 urlString 在初始化之后没有被引用到任何地方。 - Fizzix
经过长时间的努力,我终于找到了确切所需之物。非常感谢你。 - Code Tree
这似乎不适用于使用 JavaScript 打开的链接,例如 window.open(url, "_blank")。你试过能用吗? - Crashalot
1
WKNavigationType.LinkActivated会在任何链接点击时触发,而不仅仅是target="_blank"。这段代码将在Safari中打开所有链接。 - Blago

13

更新了适用于 iOS 10 Swift 3 的代码:

override func loadView() {
    super.loadView()
    self.webView.navigationDelegate = self 
    self.webView.uiDelegate = self  //must have this
}

func webView(_ webView: WKWebView,
               createWebViewWith configuration: WKWebViewConfiguration,
               for navigationAction: WKNavigationAction,
               windowFeatures: WKWindowFeatures) -> WKWebView? {
    if navigationAction.targetFrame == nil, let url = navigationAction.request.url {
      if url.description.lowercased().range(of: "http://") != nil ||
        url.description.lowercased().range(of: "https://") != nil ||
        url.description.lowercased().range(of: "mailto:") != nil {
        UIApplication.shared.openURL(url)
      }
    }
  return nil
}

这似乎不适用于使用JavaScript打开的链接,即window.open(url, "_blank") - Crashalot
1
收到错误:无法将类型“ViewController”分配给类型“WKUIDelegate”,通过使用class ViewController: UIViewController, WKNavigationDelegate, WKUIDelegate {进行了更正。 - oortCloud
我也可以确认JavaScript的window.open()对我有效。 - oortCloud
当我添加 self.webView.uiDelegate = self 时,我得到了 Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value。我在顶部有 var webView: WKWebView! - Limpuls

4

Swift 4.2

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Swift.Void) {
    if navigationAction.navigationType == WKNavigationType.linkActivated {
        print("here link Activated!!!")
        if let url = navigationAction.request.url {
            let shared = UIApplication.shared
            if shared.canOpenURL(url) {
                shared.open(url, options: [:], completionHandler: nil)
            }
        }
        decisionHandler(.cancel)
    }
    else {
        decisionHandler(.allow)
    }
}

UIApplication.shared 在 macOS 上不可用。我能修复它吗? - PascalS
试试这个:https://dev59.com/7V8d5IYBdhLWcg3wiip9#29904415 - Daniel

2
func webView(_ webView: WKWebView,
           createWebViewWith configuration: WKWebViewConfiguration,
           for navigationAction: WKNavigationAction,
           windowFeatures: WKWindowFeatures) -> WKWebView? {
  if navigationAction.targetFrame == nil, let url = navigationAction.request.url, let scheme = url.scheme {
    if ["http", "https", "mailto"].contains(where: { $0.caseInsensitiveCompare(scheme) == .orderedSame }) {
      UIApplication.shared.openURL(url)
    }
  }
  return nil
}

0

虽然上述一些解决方案在某种程度上起到了作用,但问题在于所有链接都开始在外部打开,而不仅仅是那些未遵循HTTPs URL方案的链接,比如mailto、blank、deeplinks等。

我实现了这些WKNavigationDelegate函数:

  1. decidePolicyFor navigationAction(文档链接),允许处理甚至不遵循HTTPs方案的URL

  2. 使用此导航失败委托函数webView didFailProvisionalNavigation并检查iOS是否能够处理在新标签页中打开、邮件、深度链接等,因此在您的情况下,它将打开应用商店

  3. 您还可以在此WKNavigationDelegate函数中实现与第2点相同的逻辑,以防万一

以下是代码:

class ViewController: UIViewController, WKNavigationDelegate
{
    // Initialize the webview anywhere you wish
    override func viewDidAppear(_ animated: Bool)
    {
        super.viewDidAppear(animated)
        
        let html = """
        <a href="https://apps.apple.com/us/app/directorio-notarios-cdmx/id1544000342"> App store Deep link usually doesn't open </a></span></span><br />

        <a href="https://landercorp.mx" rel="noopener"> Normal link </a></span></span><br />
        """
        
        let webview = WKWebView()
        webview.frame = view.bounds
        webview.navigationDelegate = self
        view.addSubview(webview)
        webview.loadHTMLString(html, baseURL: nil)
    }
    
    // MARK: WKNavigationDelegates
    func webView(_ webView: WKWebView,
                 decidePolicyFor navigationAction: WKNavigationAction,
                 decisionHandler: @escaping (WKNavigationActionPolicy) -> Void)
    {
        decisionHandler(.allow)
    }
    
    
    func webView(_ webView: WKWebView,
                 didFailProvisionalNavigation navigation: WKNavigation!,
                 withError error: Error)
    {
        manageFailedNavigation(webView,
                               didFail: navigation,
                               withError: error)
    }

    func webView(_ webView: WKWebView,
                 didFail navigation: WKNavigation!,
                 withError error: Error)
    {
        manageFailedNavigation(webView,
                               didFail: navigation,
                               withError: error)
    }

    private func manageFailedNavigation(_ webView: WKWebView,
                                        didFail navigation: WKNavigation!,
                                        withError error: Error)
    {
        // Check if this failed because of mailto, _blank, deep links etc
        // I have commented out how to check for a specific case like open in a new tab,
        // you can try to handle each case as you wish
        if error.localizedDescription
            == "Redirection to URL with a scheme that is not HTTP(S)"
           //let url = webView.url, url.description.lowercased().range(of: "blank") != nil
        {
            // Convert error to NSError so we can access the url
            let nsError = error as NSError
            
            // Get the url from the error
            // This key could change in future iOS releases
            if let failedURL = nsError.userInfo["NSErrorFailingURLKey"] as? URL
            {
                // Check if the action can be handled by iOS
                if UIApplication.shared.canOpenURL(failedURL)
                {
                    // Request iOS to open handle the link
                    UIApplication.shared.open(failedURL, options: [:],
                                              completionHandler: nil)
                }
            }
        }
    }
}

这样就可以处理遵循HTTPs URL方案和不遵循的两种情况。

Open WKWebview target="_blank" mailto deeplink new tab app store link


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接