如何处理由webView.uiDelegate打开的页面中的window.close操作?
针对你已经拦截window.open创建新WKWebView的场景,处理这些新页面里的window.close操作,有两个实用方案,根据你的iOS版本兼容性需求来选:
方案1:注入JS监听+原生消息回调(兼容iOS 11+)
因为网页直接调用window.close无法直接触发原生关闭WebView的逻辑,我们可以通过注入自定义脚本覆盖window.close方法,让它向原生发送一个关闭消息,原生收到后再处理WebView的销毁。
步骤:
创建WKUserScript覆盖window.close
写一段JS脚本,把默认的window.close替换成发送消息给原生的逻辑:let closeScript = WKUserScript(source: """ window.close = function() { window.webkit.messageHandlers.closeWebView.postMessage(null); }; """, injectionTime: .atDocumentStart, forMainFrameOnly: false)配置WKWebViewConfiguration并添加消息处理
在你创建新WKWebView的createWebViewWith方法里,给配置添加这个脚本,同时设置脚本消息处理器:func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? { // 添加自定义脚本 configuration.userContentController.addUserScript(closeScript) // 添加消息处理器(记得让当前类遵循WKScriptMessageHandler) configuration.userContentController.add(self, name: "closeWebView") let newWebView = WKWebView(frame: UIScreen.main.bounds, configuration: configuration) newWebView.navigationDelegate = self // 把新WebView添加到视图或者导航栈(根据你的布局逻辑) self.view.addSubview(newWebView) // 可以用字典或者数组记录这些WebView,方便后续找到对应实例关闭 self.openWebViews.append(newWebView) return newWebView }实现WKScriptMessageHandler处理关闭消息
在你的ViewController中实现userContentController(_:didReceive:)方法:func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { if message.name == "closeWebView" { // 找到发送消息的WebView实例 if let webView = message.webView { webView.removeFromSuperview() // 如果是导航栈里的页面,就用dismiss或者popViewController // self.dismiss(animated: true) // 同时从记录的数组里移除 if let index = self.openWebViews.firstIndex(of: webView) { self.openWebViews.remove(at: index) } } } }
方案2:利用WKWebView的webViewDidClose代理(iOS 14+)
从iOS14开始,WKWebView新增了webViewDidClose(_:)代理方法,当网页调用window.close时,这个回调会被触发——前提是这个WebView是通过window.open创建的(也就是你在createWebViewWith里返回的实例)。
步骤:
实现WKNavigationDelegate的webViewDidClose方法
func webViewDidClose(_ webView: WKWebView) { // 直接关闭对应的WebView webView.removeFromSuperview() // 如果是模态弹出的WebViewController,就执行dismiss // if let presentingVC = self.presentingViewController { // presentingVC.dismiss(animated: true) // } // 同样记得从你的WebView记录集合里移除 if let index = self.openWebViews.firstIndex(of: webView) { self.openWebViews.remove(at: index) } }确保新WebView的navigationDelegate设置正确
在你创建新WebView的时候,一定要把它的navigationDelegate设为当前ViewController,这样代理方法才能被触发:let newWebView = WKWebView(frame: UIScreen.main.bounds, configuration: configuration) newWebView.navigationDelegate = self
注意事项:
- 不管用哪个方案,都要管理好WebView的生命周期,避免内存泄漏——记得在WebView被移除时,移除脚本消息处理器(
userContentController.removeScriptMessageHandler(forName: "closeWebView"))。 - 如果你的新WebView是放在单独的ViewController里展示的,关闭时要对应dismiss或者pop这个ViewController,而不仅仅是移除WebView。
内容的提问来源于stack exchange,提问作者gismo141

