Swift中TableView与CollectionView双向无限滚动时CollectionView上拉加载异常问题
问题原因
- 触发条件逻辑缺陷:当前向上加载的判断条件
scrollView.contentOffset.y < scrollView.frame.height阈值过大,没有和你预设的触发距离fireRequestDistance对齐,同时缺少内容高度超过滚动视图高度的前置判断,初始状态下容易误触发。 - 组件行为差异:UITableView在头部插入新行时会自动调整
contentOffset,保持当前可视内容的位置不变,插入后contentOffset.y会同步增加新插入内容的总高度,因此不会再次满足触发条件。但UICollectionView插入头部item时不会自动调整contentOffset,插入后contentOffset.y仍保持插入前的数值,依然满足触发条件,同时你插入完成后立刻将isLoadingPrev重置为false,就会形成死循环反复插入数据。 - 动画触发额外回调:UICollectionView默认插入item时带过渡动画,动画过程中会多次触发
scrollViewDidScroll回调,进一步加剧重复触发问题。
修复方案
按照以下步骤修改代码即可解决:
- 统一上下加载的触发逻辑,向上加载也使用
fireRequestDistance作为阈值,同时新增内容高度判断避免初始误触发 - 插入头部数据后手动调整UICollectionView的
contentOffset,保持可视位置不变的同时避免再次命中触发条件 - UICollectionView插入操作禁用动画,和UITableView行为保持一致
- 调整
isLoadingPrev的重置时机,确保所有操作完成后再重置
修改后的CollectionViewController的scrollViewDidScroll代码如下:
func scrollViewDidScroll(_ scrollView: UIScrollView) { // 向下加载逻辑优化(新增禁用动画配置) if !isLoadingNext, scrollView.contentOffset.y + scrollView.bounds.height > scrollView.contentSize.height - fireRequestDistance { isLoadingNext = true let max = self.items.last! let newItems = Array((max + 1)...(max + numberOfItemsPerRequest)) let indexPaths = (self.items.count..<(self.items.count + numberOfItemsPerRequest)).map { IndexPath(row: $0, section: 0) } self.items += newItems // 禁用插入动画,和UITableView行为统一 UIView.performWithoutAnimation { self.scrollitem.insertItems(at: indexPaths) } self.isLoadingNext = false } // 重构后的向上加载逻辑 if !isLoadingPrev, scrollView.contentSize.height > scrollView.bounds.height, scrollView.contentOffset.y < fireRequestDistance { isLoadingPrev = true let min = self.items.first! let newItems = Array((min - numberOfItemsPerRequest)..<min) let indexPaths = (0..<numberOfItemsPerRequest).map { IndexPath(row: $0, section: 0) } // 插入前记录当前内容高度 let oldContentHeight = scrollView.contentSize.height items = newItems + self.items // 禁用插入动画 UIView.performWithoutAnimation { self.scrollitem.insertItems(at: indexPaths) } // 计算新插入内容的高度,手动调整偏移量保持可视位置不变 let newContentHeight = scrollView.contentSize.height let addedHeight = newContentHeight - oldContentHeight scrollView.contentOffset.y += addedHeight // 所有操作完成后再重置加载状态 self.isLoadingPrev = false } }
上述方案对固定高度、动态高度的UICollectionViewCell都通用,不需要额外适配布局逻辑。
内容的提问来源于stack exchange,提问作者Evgeny
相关产品推荐
相关产品推荐

