如何禁用iPad端WKWebView中点击电话号码弹出的弹窗?
如何在iPad上拦截WKWebView中的电话号码点击并执行自定义操作?
我之前也踩过这个iPad上的坑——iPhone上用WKNavigationDelegate的decidePolicyFor方法能顺利拦截电话链接的点击,但到了iPad上,系统直接弹出拨号弹窗,那个代理方法根本没机会触发!下面分享两个亲测有效的解决方案:
方案一:注入JavaScript提前拦截电话链接点击
因为iPad上系统会直接识别tel:链接并触发系统动作,完全不走导航代理流程,所以我们可以通过注入JS代码,在网页层面提前拦截所有电话链接的点击事件,再把消息传递给原生代码执行自定义操作。
步骤1:在网页加载完成后注入拦截JS
实现WKNavigationDelegate的didFinish方法,注入监听点击的脚本:
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { // 监听页面所有点击事件,拦截tel:链接 let interceptPhoneJS = """ document.addEventListener('click', function(e) { let targetElement = e.target; // 向上遍历找到a标签 while (targetElement && targetElement.tagName !== 'A') { targetElement = targetElement.parentNode; } // 判断是否是电话链接 if (targetElement && targetElement.href && targetElement.href.startsWith('tel:')) { e.preventDefault(); // 阻止系统默认行为 // 把链接传递给原生 window.webkit.messageHandlers.phoneLinkClicked.postMessage(targetElement.href); } }, true); """ webView.evaluateJavaScript(interceptPhoneJS) { result, error in if let error = error { print("注入拦截脚本失败:\(error.localizedDescription)") } } }
步骤2:配置WKUserContentController接收JS消息
在初始化WKWebView时,添加消息处理器,用来接收JS传递的电话链接:
// 初始化WKUserContentController let userContentController = WKUserContentController() // 添加消息处理器,name要和JS里的一致 userContentController.add(self, name: "phoneLinkClicked") // 配置WKWebView let webConfig = WKWebViewConfiguration() webConfig.userContentController = userContentController // 创建WKWebView let webView = WKWebView(frame: view.bounds, configuration: webConfig) webView.navigationDelegate = self view.addSubview(webView)
步骤3:实现WKScriptMessageHandler协议处理消息
让你的ViewController遵守WKScriptMessageHandler协议,接收并处理JS传来的电话链接:
extension YourViewController: WKScriptMessageHandler { func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { guard message.name == "phoneLinkClicked", let telUrl = message.body as? String else { return } // 提取电话号码,执行自定义操作 let phoneNumber = telUrl.replacingOccurrences(of: "tel:", with: "") print("用户点击了电话号码:\(phoneNumber)") // 这里可以写你的自定义逻辑,比如弹出自己的确认弹窗,或者直接调用拨号 // 示例:UIAlertController确认拨号 let alert = UIAlertController(title: "拨打电话", message: phoneNumber, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "取消", style: .cancel)) alert.addAction(UIAlertAction(title: "拨号", style: .default) { _ in if let callUrl = URL(string: telUrl) { UIApplication.shared.open(callUrl) } }) present(alert, animated: true) } }
方案二:禁用WKWebView的自动电话识别(简单但有局限性)
如果你不需要网页自动把文本识别为电话链接(比如不需要显示下划线样式),可以直接关闭WKWebView的自动电话检测功能,这样点击电话链接就会走decidePolicyFor代理方法了,和iPhone上的行为一致。
代码实现
初始化WKWebView时修改配置:
let webConfig = WKWebViewConfiguration() // 移除自动检测电话号码的类型 webConfig.dataDetectorTypes.remove(.phoneNumber) let webView = WKWebView(frame: view.bounds, configuration: webConfig) webView.navigationDelegate = self view.addSubview(webView)
然后就可以用你原来的decidePolicyFor方法拦截了:
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { guard let url = navigationAction.request.url, url.scheme == "tel" else { decisionHandler(.allow) return } // 执行你的自定义操作 let phoneNumber = url.absoluteString.replacingOccurrences(of: "tel:", with: "") print("拦截到电话链接:\(phoneNumber)") // 取消系统默认导航,执行自己的逻辑 decisionHandler(.cancel) // 示例自定义逻辑:弹出确认弹窗 let alert = UIAlertController(title: "确认拨号", message: phoneNumber, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "取消", style: .cancel)) alert.addAction(UIAlertAction(title: "拨号", style: .default) { _ in UIApplication.shared.open(url) }) present(alert, animated: true) }
注意
这个方案的缺点是:网页里的纯文本电话号码不会被自动识别为可点击的链接了,如果你的业务需要保留这个样式,建议用方案一。
内容的提问来源于stack exchange,提问作者Nick
相关产品推荐
相关产品推荐

