如何在UICollectionView滚动到顶部完成后执行操作?
UICollectionView滚动到顶部并执行完成回调的几种实现方式
这个需求我太熟悉了!iOS原生的UICollectionView滚动方法(不管是setContentOffset、scrollRectToVisible还是scrollToItem)确实都没提供原生的完成回调,但我们有几个巧妙的办法可以解决,既保留原生动画,又能拿到结束通知:
方法一:利用UIScrollView代理方法
因为UICollectionView继承自UIScrollView,所以可以借助它的滚动代理来判断动画是否结束。思路是用一个标记变量来标记这次滚动是否需要触发后续操作:
- 先定义一个私有变量作为标记:
private var shouldTriggerCompletion = false
- 调用滚动方法前设置标记:
func scrollToTopWithCompletion() { // 先判断集合视图有内容,避免scrollToItem崩溃 guard collectionView.numberOfSections > 0, collectionView.numberOfItems(inSection: 0) > 0 else { // 没有内容的话直接执行后续操作或者返回 executePostScrollAction() return } shouldTriggerCompletion = true let topIndexPath = IndexPath(item: 0, section: 0) collectionView.scrollToItem(at: topIndexPath, at: .top, animated: true) }
- 实现UIScrollView的代理方法,触发后续操作:
extension YourViewController: UIScrollViewDelegate { func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { guard shouldTriggerCompletion else { return } executePostScrollAction() shouldTriggerCompletion = false } // 额外处理用户手动打断滚动的情况(可选) func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { guard shouldTriggerCompletion else { return } executePostScrollAction() shouldTriggerCompletion = false } } // 封装你要执行的后续操作 private func executePostScrollAction() { print("滚动完成!开始执行后续任务") // 这里写你的业务逻辑 }
优点:完全保留原生滚动动画,逻辑清晰;缺点:需要依赖代理,而且如果用户在滚动动画过程中手动拖动打断,需要额外处理scrollViewDidEndDecelerating。
方法二:使用CATransaction的完成回调
UICollectionView的滚动动画本质是Core Animation动画,所以我们可以把滚动操作放到CATransaction中,设置全局的完成回调:
func scrollToTopWithCompletion() { // 处理空数据情况 guard collectionView.numberOfSections > 0, collectionView.numberOfItems(inSection: 0) > 0 else { executePostScrollAction() return } CATransaction.begin() CATransaction.setCompletionBlock { [weak self] in self?.executePostScrollAction() } let topIndexPath = IndexPath(item: 0, section: 0) collectionView.scrollToItem(at: topIndexPath, at: .top, animated: true) // 如果用setContentOffset的话: // let topOffset = CGPoint(x: 0, y: -collectionView.contentInset.top) // collectionView.setContentOffset(topOffset, animated: true) CATransaction.commit() } private func executePostScrollAction() { print("滚动完成,执行后续操作") }
优点:不需要依赖代理,代码更集中;不管动画是正常结束还是被用户手动中断,都会触发完成回调;注意点:记得用[weak self]避免循环引用。
额外注意事项
- 滚动到顶部时,记得考虑
contentInset的影响,比如如果有导航栏或者顶部内边距,setContentOffset的y值应该设为-collectionView.contentInset.top,否则可能滚不到真正的顶部。 - 一定要先判断集合视图是否有内容(section和item数量大于0),否则
scrollToItem会直接崩溃。
内容的提问来源于stack exchange,提问作者TheEye
相关产品推荐
相关产品推荐

