求助:在WKWebView中拦截修改粘贴内容及脚本消息未触发问题
问题解决思路与实现方案
先直接给你解决核心问题,再拆解细节:
问题1:didReceiveScriptMessage未触发的原因
你的JavaScript事件绑定写错了!addEventListener接收的事件名是paste,而不是onpaste——onpaste是DOM0级事件的写法(比如document.onpaste = function() {}),用addEventListener时必须去掉前缀on。
另外要确保WKWebView的配置里启用了JavaScript(默认是开启的,但显式设置更稳妥)。
问题2:拦截并替换粘贴内容的两种可行方案
根据你需要执行耗时异步操作的需求,推荐用「JS拦截事件 + 原生处理内容 + JS插入结果」的协作方案,下面是Swift的完整实现:
步骤1:配置WKWebView并注入正确的监听脚本
import WebKit class YourViewController: UIViewController { private var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() // 配置用户内容控制器 let userContentController = WKUserContentController() // 修正后的粘贴监听JS:拦截事件、阻止默认行为、发送内容给原生 let pasteJSSource = """ document.addEventListener('paste', function(event) { // 先阻止默认粘贴,避免原始内容直接插入 event.preventDefault(); // 获取剪贴板的文本内容(富文本用'text/html') const pasteContent = event.clipboardData.getData('text'); // 把内容发给原生App window.webkit.messageHandlers.ComposerListener.postMessage({ type: 'paste', content: pasteContent }); }); """ let pasteScript = WKUserScript(source: pasteJSSource, injectionTime: .atDocumentEnd, forMainFrameOnly: false) // 设为false监听所有iframe的粘贴 userContentController.add(self, name: "ComposerListener") userContentController.addUserScript(pasteScript) // 初始化WebView配置 let webViewConfig = WKWebViewConfiguration() webViewConfig.userContentController = userContentController webViewConfig.preferences.javaScriptEnabled = true // 显式启用JS // 创建WebView webView = WKWebView(frame: view.bounds, configuration: webViewConfig) webView.autoresizingMask = [.flexibleWidth, .flexibleHeight] view.addSubview(webView) // 加载测试页面(替换成你的目标页面) webView.load(URLRequest(url: URL(string: "https://example.com")!)) } // 销毁时移除消息处理器,避免内存泄漏 deinit { webView.configuration.userContentController.removeScriptMessageHandler(forName: "ComposerListener") } }
步骤2:实现WKScriptMessageHandler协议处理消息
extension YourViewController: WKScriptMessageHandler { func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { guard message.name == "ComposerListener", let body = message.body as? [String: Any], let type = body["type"] as? String, let rawContent = body["content"] as? String else { return } if type == "paste" { print("收到粘贴内容:\(rawContent)") // 执行耗时异步操作(比如网络请求、复杂计算) DispatchQueue.global(qos: .background).async { // 模拟耗时处理:比如把内容转成大写 let processedContent = "【处理后】\(rawContent.uppercased())" // 回到主线程,把处理后的内容插入到WebView光标位置 DispatchQueue.main.async { let insertScript = """ const selection = window.getSelection(); if (selection.rangeCount > 0) { const range = selection.getRangeAt(0); range.deleteContents(); const textNode = document.createTextNode('\(processedContent)'); range.insertNode(textNode); // 调整光标到内容末尾,提升用户体验 range.setStartAfter(textNode); range.setEndAfter(textNode); selection.removeAllRanges(); selection.addRange(range); } """ self.webView.evaluateJavaScript(insertScript, completionHandler: nil) } } } } }
备选方案:纯原生监听(无需JS)
如果你不想用JavaScript,也可以通过监听UIPasteboard的变化来实现,但这种方式无法精准判断粘贴操作是否发生在WKWebView内,适合全局监听场景:
// 在viewDidLoad中添加监听 NotificationCenter.default.addObserver(self, selector: #selector(pasteboardChanged), name: UIPasteboard.changedNotification, object: nil) // 处理剪贴板变化 @objc private func pasteboardChanged() { guard let pasteContent = UIPasteboard.general.string else { return } // 执行耗时操作,之后手动修改剪贴板内容(用户粘贴时会用修改后的内容) DispatchQueue.global().async { let processedContent = "处理后:\(pasteContent)" DispatchQueue.main.async { UIPasteboard.general.string = processedContent } } }
内容的提问来源于stack exchange,提问作者Kappe
相关产品推荐
相关产品推荐

