点击UICollectionView Cell中WKWebView导致回调不触发的问题
看起来你遇到的是WebView和UICollectionView交互时的事件冲突问题——WebView作为一个复杂的容器视图,很容易打断UICollectionView的触摸事件链,进而干扰它对cell生命周期的追踪。我来给你几个针对性的解决方案:
方案一:让WebView不吞噬触摸事件(快速修复)
WebView默认会拦截所有触摸事件,导致UICollectionView无法感知到用户的触摸操作,进而停止触发willDisplay/didEndDisplaying这类回调。你可以给WebView添加一个不拦截事件的手势识别器,让事件能传递到UICollectionView:
在你的自定义cell里,配置WebView的时候加上这段代码:
// 给WebView添加一个不拦截触摸的点击手势 let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleWebViewTap)) tapGesture.cancelsTouchesInView = false webView.addGestureRecognizer(tapGesture) // 空实现即可,只是为了让事件继续传递 @objc private func handleWebViewTap() {}
这样设置后,WebView依然能正常响应链接点击,同时UICollectionView也能接收到触摸事件,维持正常的cell生命周期回调。
方案二:修复滚动冲突+恢复预加载(推荐)
你之前关闭预加载是为了解决cell坐标异常的问题,但其实这个异常的根源是WebView的滚动和UICollectionView的分页滚动冲突,导致布局计算出错。我们可以通过控制WebView的滚动来解决,同时恢复预加载提升性能:
情况1:WebView不需要滚动(仅显示文本链接)
如果你的WebView只是用来展示带链接的文本,不需要自身滚动,直接禁用它的滚动功能:
webView.scrollView.isScrollEnabled = false
然后把collectionView.isPrefetchingEnabled改回true,这样既解决了初始的空白问题,又能保证所有回调正常触发。
情况2:WebView需要滚动
如果WebView必须支持滚动,那就处理嵌套滚动的冲突,避免两者的滚动互相干扰:
- 让你的ViewController或自定义cell实现
UIScrollViewDelegate - 添加以下代理方法:
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { // 当用户开始拖动WebView时,禁止UICollectionView滚动 if scrollView == webView.scrollView { collectionView.isScrollEnabled = false } } func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { // 当WebView滚动结束后,恢复UICollectionView的滚动能力 if scrollView == webView.scrollView { collectionView.isScrollEnabled = true } }
这样处理后,用户在操作WebView时不会误触切换页面,UICollectionView的布局和回调也能正常工作。
方案三:手动追踪cell可见性(兜底方案)
如果上面的方案都无法解决,你可以通过监听UICollectionView的滚动偏移,手动判断cell是否滑出屏幕,进而关闭模态框:
在你的ViewController里添加这段代码:
private var currentVisibleIndex = 0 override func viewDidLoad() { super.viewDidLoad() // 监听contentOffset变化 collectionView.addObserver(self, forKeyPath: "contentOffset", options: .new, context: nil) } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { guard keyPath == "contentOffset", let collectionView = object as? UICollectionView else { super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) return } // 获取当前可见的第一个cell的索引 guard let firstVisibleIndex = collectionView.indexPathsForVisibleItems.first?.item else { return } // 如果索引变化,说明之前的cell滑出了屏幕 if firstVisibleIndex != currentVisibleIndex { // 找到之前的cell,关闭它的模态框 if let previousCell = collectionView.cellForItem(at: IndexPath(item: currentVisibleIndex, section: 0)) as? YourCustomCell { previousCell.closeModalTextView() // 这里需要你在cell里实现关闭模态框的方法 } currentVisibleIndex = firstVisibleIndex } } deinit { // 记得移除观察者 collectionView.removeObserver(self, forKeyPath: "contentOffset") }
这个方法不依赖UICollectionView的回调,完全手动追踪,适合极端情况。
内容的提问来源于stack exchange,提问作者lev4

