UICollectionView自定义scrollToItem动画时Cell消失问题咨询
这个问题我之前踩过坑,本质是两个动画系统的冲突导致的,具体原因可以拆解成这几点:
scrollToItem本身自带动画逻辑
UICollectionView的scrollToItem(at:at:animated:)方法内部已经集成了一套和自身布局系统协同的滚动动画。当你把它包裹在UIView.animate的block里时,相当于同时触发了两套动画:一套是UIView对视图属性的通用动画,另一套是collectionView自身的滚动动画。这两套动画的 timing、曲线和布局同步逻辑不兼容,就会出现混乱。UIView.animate会干扰CollectionView的布局判断
如果你在UIView.animate里把scrollToItem的animated参数设为false,UIView的动画系统会直接尝试平滑修改collectionView的contentOffset。但collectionView的布局系统(比如FlowLayout)无法跟上这个外部动画的节奏,会错误地判定那些还在滚动路径上的cell已经移出了可视区域,提前把它们回收到复用池里,所以你会看到cell先消失,之后滚动动画才完成。
正确的解决方式
方式1:直接用scrollToItem自带的动画
如果只是需要基础的滚动动画,完全不需要套UIView.animate,直接开启自带的动画参数即可:
// 直接使用collectionView自带的滚动动画,和布局系统完全兼容 collectionView.scrollToItem(at: targetIndexPath, at: .centeredHorizontally, animated: true)
方式2:自定义动画时用contentOffset手动控制
如果需要自定义动画时长、弹簧效果这类高级需求,不要用scrollToItem,而是直接操作collectionView的contentOffset,因为它是UIScrollView的子类,支持通过UIView.animate来平滑修改这个属性:
// 先计算目标位置的contentOffset guard let targetAttrs = collectionView.layoutAttributesForItem(at: targetIndexPath) else { return } let targetOffset = CGPoint(x: targetAttrs.frame.origin.x - collectionView.contentInset.left, y: 0) // 用UIView.animate直接修改contentOffset,避免和scrollToItem的动画冲突 UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.3) { self.collectionView.contentOffset = targetOffset }
这种方式下,collectionView的布局系统会跟着contentOffset的动画节奏正确更新cell的可见状态,不会出现提前消失的问题。
内容的提问来源于stack exchange,提问作者Ninja

