在WKWebView中启动电话/电子邮件/地图链接

24

KINWebBrowser是一个开源的iOS应用程序浏览器模块。我最近升级了KINWebBrowser,使用WKWebView来逐步淘汰UIWebView。这样可以显著提高性能,但是:

问题: WKWebView不支持启动包含电话号码、电子邮件地址、地图等URL的链接。

如何配置WKWebView,在从页面中启动这些替代URL链接时,以启动标准的iOS行为?

所有代码都在这里

更多关于WKWebKit的信息

请查看KINWebBrowser GitHub上的问题


2
你无法这样做。如果这个功能对你很重要,那么现在坚持使用UIWebView是有理由的——同时向苹果提出增强请求。UIWebView能做很多WKWebView不能做的事情。 - matt
7个回答

27

通过将此函数添加到KINWebBrowserViewController.m中,我成功让Google Maps链接(似乎与target="_blank"相关)和tel:方案正常工作。

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
    if(webView != self.wkWebView) {
        decisionHandler(WKNavigationActionPolicyAllow);
        return;
    }

    UIApplication *app = [UIApplication sharedApplication];
    NSURL         *url = navigationAction.request.URL;

    if (!navigationAction.targetFrame) {
        if ([app canOpenURL:url]) {
            [app openURL:url];
            decisionHandler(WKNavigationActionPolicyCancel);
            return;
        }
    }
    if ([url.scheme isEqualToString:@"tel"])
    {
        if ([app canOpenURL:url])
        {
            [app openURL:url];
            decisionHandler(WKNavigationActionPolicyCancel);
            return;
        }
    }
    decisionHandler(WKNavigationActionPolicyAllow);
}

2
这段代码有点啰嗦,并且不能保证调用decisionHandler - Aaron Brager
好的观点!谢谢。这是一个深夜,最后一分钟从两个不同的来源拼凑在一起,以快速解决问题的补丁。虽然它确实起作用了,但并不是最干净的,肯定有一些情况下 decisionHandler 没有被调用。我已经编辑了代码。谢谢! - Darren Ehlers
2
这段代码肯定是在正确的道路上,但它引入了一个安全漏洞,可能会导致不愿意发起的电话和FaceTime通话。请查看我的解释:https://github.com/dfmuir/KINWebBrowser/issues/10 - dfmuir
非常好的发现。在我的情况下,该应用程序仅显示受严密控制的网页(供内部信息查看),因此我们目前是安全的。但是同样是一个很棒的观点! - Darren Ehlers
1
这个解决方案在我们公司非常有帮助。谢谢Darren。我们稍微修改了一下,还能够从我们网页上的链接打开应用商店,并从网页上点击链接打开邮件应用程序。干杯! - Stephen Paul
2
为了避免@dfmuir指出的安全漏洞问题,请将URL更改为“telprompt”,而不是默认的“tel”。例如,如果原始自定义URL为“tel:(212)%555-6666”,则telprompt等效为“telprompt:(212)%555-6666”。这样用户在拨打电话之前会得到提示。 - Harry Wang

13

适用于xcode 8.1和Swift 2.3。

针对"_blank"、电话号码(tel:)和电子邮件(mailto:)链接。

func webView(webView: WKWebView, decidePolicyForNavigationAction navigationAction: WKNavigationAction, decisionHandler: (WKNavigationActionPolicy) -> Void) {
    if webView != self.webview {
        decisionHandler(.Allow)
        return
    }

    let app = UIApplication.sharedApplication()
    if let url = navigationAction.request.URL {
        // Handle target="_blank"
        if navigationAction.targetFrame == nil {
            if app.canOpenURL(url) {
                app.openURL(url)
                decisionHandler(.Cancel)
                return
            }
        }

        // Handle phone and email links
        if url.scheme == "tel" || url.scheme == "mailto" {
            if app.canOpenURL(url) {
                app.openURL(url)
                decisionHandler(.Cancel)
                return
            }
        }

        decisionHandler(.Allow)
    }
}

更新了Swift 4.0

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

    if webView != self.webView {
        decisionHandler(.allow)
        return
    }

    let app = UIApplication.shared
    if let url = navigationAction.request.url {
        // Handle target="_blank"
        if navigationAction.targetFrame == nil {
            if app.canOpenURL(url) {
                app.open(url)
                decisionHandler(.cancel)
                return
            }
        }

        // Handle phone and email links
        if url.scheme == "tel" || url.scheme == "mailto" {
            if app.canOpenURL(url) {
                app.open(url)
            }

            decisionHandler(.cancel)
            return
        }

        decisionHandler(.allow)
    }

}

不要忘记添加代理:WKUIDelegate和var webView = WKWebView()到属性中。 - Victor Almeida

9

您需要实现另一个回调函数以正确获取此内容(Swift 5.0):

// Gets called if webView cant handle URL
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
  guard let failingUrlStr = (error as NSError).userInfo["NSErrorFailingURLStringKey"] as? String  else { return }
  let failingUrl = URL(string: failingUrlStr)!

  switch failingUrl {
    // Needed to open Facebook
    case _ where failingUrlStr.hasPrefix("fb:"):
    if #available(iOS 10.0, *) {
       UIApplication.shared.open(failingUrl, options: [:], completionHandler: nil)
       return
    } // Else: Do nothing, iOS 9 and earlier will handle this

  // Needed to open Mail-app
  case _ where failingUrlStr.hasPrefix("mailto:"):
    if UIApplication.shared.canOpenURL(failingUrl) {
      UIApplication.shared.open(failingUrl, options: [:], completionHandler: nil)
      return
    }

  // Needed to open Appstore-App
  case _ where failingUrlStr.hasPrefix("itmss://itunes.apple.com/"):
    if UIApplication.shared.canOpenURL(failingUrl) {
      UIApplication.shared.open(failingUrl, options: [:], completionHandler: nil)
      return
    }

  default: break
  }
}

现在你可以直接从你的应用程序中调用Facebook、Mail、Appstore等应用程序,而无需打开Safari。
编辑:使用标准的hasPrefix()方法代替自定义的startsWith()方法。

什么是startsWith? - Slaknation
它与 hasPrefix() 相同。 - 2h4u
但是为什么Tel方案无法加载? - Pankaj Battise

0

我是在搜索如何在wkwebview上打开gmail附件时来到这里的。

我的解决方案很简单:

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
    if navigationAction.targetFrame == nil, let redirect = navigationAction.request.url {
        if UIApplication.shared.canOpenURL(redirect) {
            self.webViewMail?.load(navigationAction.request)
            decisionHandler(.cancel)
            return
        }
    }
    decisionHandler(.allow)
}

0

这对我在Xcode 8 WKWebview中很有帮助

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.range(of: "http://") != nil || url?.description.range(of: "https://") != nil || url?.description.range(of: "mailto:") != nil || url?.description.range(of: "tel:") != nil  {
            UIApplication.shared.openURL(url!)
        }
    }
    return nil
}

编辑:

链接中必须包含属性target="_blank"


0

Swift 4.2 更新

抱歉挖出一个旧帖子,但我遇到了同样的问题,并更新了 Swift 4.2 的解决方案。我将我的解决方案放在这里,以便它可以帮助其他人,如果不能,我希望下次使用 WKWebView 时能找到它!

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

    let url = navigationAction.request.url?.absoluteString
    let urlElements = url?.components(separatedBy: ":") ?? []

    switch urlElements[0] {

    case "tel":
        UIApplication.shared.openURL(navigationAction.request.url!)
        decisionHandler(.cancel)
    case "mailto":
        UIApplication.shared.openURL(navigationAction.request.url!)
        decisionHandler(.cancel)
    default:
        decisionHandler(.allow)
    }
}

我以以下网站为灵感:

SubzDesignz iOS Swift 4 WKWebview – Detect tel, mailto, target=”_blank” and CheckConnection


-1

上面的答案对我有用,但我需要将其重写为Swift 2.3

if navigationAction.targetFrame == nil {
    let url = navigationAction.request.mainDocumentURL
    if url?.description.rangeOfString("mailto:")?.startIndex != nil ||
        url?.description.rangeOfString("tel:")?.startIndex != nil
    {
        if #available(iOS 10, *) {
            UIApplication.sharedApplication().openURL(url!,options: [:], completionHandler: nil)
        } else {
            UIApplication.sharedApplication().openURL(url!)  // deprecated
        }
    }
}

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