Swift项目中UIScrollView嵌套UICollectionView后垂直滚动失效问题
嵌套滚动手势冲突解决方案
问题原因
该问题由UIScrollView家族控件的默认手势拦截机制导致:UICollectionView本身继承自UIScrollView,触发触摸事件时会默认优先拦截手势,没有开启和父层级滚动视图的手势同步识别能力,因此垂直滑动手势会被水平UICollectionView拦截,无法传递到外层垂直UIScrollView。
你设置collectionView.isUserInteractionEnabled = false后相当于完全关闭了collectionView的交互响应能力,手势会直接透传到父视图,所以外层滚动恢复正常,但该方案会导致collectionView的水平滚动、cell点击等交互全部失效,不推荐业务场景使用。
最优解决方案(保留collectionView全部交互能力)
通过手势代理开启双滚动视图的手势同步识别能力即可,步骤如下:
- 第一步:让当前UIViewController遵守
UIGestureRecognizerDelegate协议 - 第二步:给collectionView的拖动手势设置代理
collectionView.panGestureRecognizer.delegate = self - 第三步:实现手势同步识别代理方法
通用版(直接允许同步识别,系统会自动根据滑动方向分配响应者):
精准控制版(仅垂直滑动时透传手势,水平滑动时完全由collectionView响应,体验更流畅):func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { // 仅当两个手势分别属于内部collectionView和外层scrollView时才开启同步识别 if (gestureRecognizer.view == collectionView && otherGestureRecognizer.view == outerScrollView) || (otherGestureRecognizer.view == collectionView && gestureRecognizer.view == outerScrollView) { return true } return false }func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { guard let panGesture = gestureRecognizer as? UIPanGestureRecognizer, panGesture.view == collectionView, otherGestureRecognizer.view == outerScrollView else { return false } let velocity = panGesture.velocity(in: view) // 垂直方向滑动速度大于水平方向时,允许同步识别 return abs(velocity.y) > abs(velocity.x) }
补充验证项
如果配置后仍不生效,请检查以下配置:
- 确认collectionView的滚动方向为水平,且
collectionView.contentSize.height等于collectionView.frame.height,避免collectionView自身支持垂直滚动 - 确认外层UIScrollView的
contentSize.height大于自身frame高度,本身支持垂直滚动
内容的提问来源于stack exchange,提问作者Георгий Щипин
相关产品推荐
相关产品推荐

