UICollectionView中performBatchUpdates动画:如何移除淡入淡出效果?
解决集合视图选中section后的动画需求
针对你需要的「选中section后其他section立即消失,目标section平滑滑到顶部」的需求,下面逐个回答你的问题并给出可行方案:
1. 让删除和移动动画顺序执行
系统默认的performBatchUpdates会把所有更新操作打包成一组动画同时执行,要拆分顺序的话,可以分两次执行更新,且第一次删除操作禁用动画:
// 先更新数据源为仅保留目标section data = [targetSectionData] // 第一步:立即删除多余section,关闭动画 UIView.performWithoutAnimation { coll.performBatchUpdates({ coll.deleteSections(removeIndexPathSections) }, completion: nil) } // 第二步:触发目标section的滑动动画 // 注意:删除后目标section的索引已变为0,此时可以直接滚动到该section的顶部 coll.scrollToItem(at: IndexPath(item: 0, section: 0), at: .top, animated: true)
这里用
scrollToItem代替moveSection,因为删除其他section后目标section已经处于索引0的位置,moveSection不会产生任何视觉变化,直接滚动更直观。
2. 分别设置两种动画的时长
系统的performBatchUpdates不支持单独为不同操作设置动画时长,所有批量更新共享同一套动画参数(时长、曲线)。要实现不同时长的效果,只能通过拆分操作+手动动画的方式模拟,无法直接通过系统API实现。
3. 其他可行方案
方案一:隐藏cell+手动滚动动画
这是对你临时方案的优化,减少复用问题的影响:
// 1. 立即隐藏不需要的section的所有cell for section in removeIndexPathSections { let itemCount = coll.numberOfItems(inSection: section) for item in 0..<itemCount { coll.cellForItem(at: IndexPath(item: item, section: section))?.isHidden = true } } // 2. 计算目标section顶部的偏移量 let targetIndexPath = IndexPath(item: 0, section: indexPathOfTargetRow) let targetRect = coll.layoutAttributesForItem(at: targetIndexPath)?.frame ?? .zero let targetOffset = CGPoint( x: 0, y: targetRect.origin.y - coll.contentInset.top - coll.safeAreaInsets.top ) // 3. 执行滑动动画(时长0.5秒) UIView.animate(withDuration: 0.5) { coll.contentOffset = targetOffset } completion: { _ in // 4. 动画结束后更新数据源并刷新,确保视图与数据一致 self.data = [self.targetSectionData] self.coll.reloadData() completion() }
注意:在cell的
prepareForReuse中务必添加isHidden = false,避免复用后cell异常隐藏。
方案二:快照视图模拟动画(推荐,无复用问题)
通过生成目标cell的快照,模拟滑动动画,完全避开cell复用的麻烦:
// 1. 获取目标cell并生成快照 guard let targetCell = coll.cellForItem(at: IndexPath(item: 0, section: indexPathOfTargetRow)) else { completion() return } let snapshot = targetCell.snapshotView(afterScreenUpdates: true)! snapshot.frame = coll.convert(targetCell.frame, to: coll.superview) coll.superview?.addSubview(snapshot) // 2. 隐藏所有cell,让其他section立即消失 for section in 0..<coll.numberOfSections { for item in 0..<coll.numberOfItems(inSection: section) { coll.cellForItem(at: IndexPath(item: item, section: section))?.isHidden = true } } // 3. 计算顶部目标位置 let targetFrame = CGRect( x: coll.contentInset.left + coll.safeAreaInsets.left, y: coll.contentInset.top + coll.safeAreaInsets.top, width: coll.bounds.width - coll.contentInset.left - coll.contentInset.right - coll.safeAreaInsets.left - coll.safeAreaInsets.right, height: targetCell.frame.height ) let targetFrameInSuperview = coll.convert(targetFrame, to: coll.superview) // 4. 执行滑动动画 UIView.animate(withDuration: 0.5) { snapshot.frame = targetFrameInSuperview } completion: { _ in // 5. 更新数据源、刷新视图并移除快照 self.data = [self.targetSectionData] self.coll.reloadData() snapshot.removeFromSuperview() completion() }
内容的提问来源于stack exchange,提问作者Fattie
相关产品推荐
相关产品推荐

