如何检测WKWebView中AMP页面的滚动?代理方法失效的解决方案
哈哈,这个坑我踩过!AMP页面的滚动逻辑和普通网页不一样——它会在内部创建自己的滚动容器,根本不会用到WKWebView自带的UIScrollView,所以你绑定原生scrollView代理的方式自然就抓不到滚动事件了。给你分享几个实测有效的解决方案:
解决方案一:通过JS注入监听AMP内部滚动容器并与原生通信
这是最直接有效的方法,核心思路是用JavaScript找到AMP的内部滚动容器,监听它的滚动事件,再通过WKWebView的脚本消息机制把滚动数据传递给原生代码。
步骤1:配置WKWebView的脚本消息处理器
首先要让WKWebView能接收JS发来的消息,初始化时添加脚本处理器:
let webConfig = WKWebViewConfiguration() let userContentController = WKUserContentController() // 注册一个名为"ampScrollHandler"的消息处理器,后续JS会通过这个名字发消息 userContentController.add(self, name: "ampScrollHandler") webConfig.userContentController = userContentController // 初始化WKWebView self.myWebView = WKWebView(frame: self.view.bounds, configuration: webConfig) self.myWebView.scrollView.delegate = self // 保留原来的代理,兼容普通网页 self.view.addSubview(self.myWebView)
步骤2:在网页加载完成后注入监听JS
等AMP页面加载完成后,注入JS找到滚动容器并绑定滚动事件。推荐监听AMP官方的amp-ready事件,比固定延迟更靠谱:
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { // 注入的JS代码 let scrollMonitorJS = """ document.addEventListener('amp-ready', () => { // AMP页面的滚动容器标准类名是i-amphtml-scroll-container const scrollContainer = document.querySelector('.i-amphtml-scroll-container'); if (scrollContainer) { // 监听滚动事件 scrollContainer.addEventListener('scroll', () => { // 把滚动数据发给原生 window.webkit.messageHandlers.ampScrollHandler.postMessage({ scrollTop: scrollContainer.scrollTop, scrollHeight: scrollContainer.scrollHeight, clientHeight: scrollContainer.clientHeight }); }); } }); """ // 执行JS注入 webView.evaluateJavaScript(scrollMonitorJS) { result, error in if let error = error { print("AMP滚动监听JS注入失败:\(error.localizedDescription)") } } }
步骤3:实现原生端的消息接收逻辑
让你的ViewController遵守WKScriptMessageHandler协议,接收JS发来的滚动数据:
extension YourViewController: WKScriptMessageHandler { func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { guard message.name == "ampScrollHandler", let scrollData = message.body as? [String: Int] else { return } let scrollTop = scrollData["scrollTop"] ?? 0 let scrollHeight = scrollData["scrollHeight"] ?? 0 let clientHeight = scrollData["clientHeight"] ?? 0 // 这里写你的滚动处理逻辑,比如判断是否滚动到底部 let isAtBottom = (scrollTop + clientHeight) >= scrollHeight - 10 // 留10px的误差 print("AMP页面滚动状态:scrollTop = \(scrollTop),是否到底部:\(isAtBottom)") } }
补充说明
- 为什么不用原生scrollView代理?因为AMP页面会把自己的内容放在固定高度的容器里,内部自己处理滚动,WKWebView的scrollView根本不会动,所以原生代理自然监听不到。
- 如果遇到特殊AMP页面滚动容器类名不一样?可以用浏览器开发者工具查看页面结构,找到实际的滚动容器元素,调整JS里的选择器即可。
内容的提问来源于stack exchange,提问作者Randy
相关产品推荐
相关产品推荐

