You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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回调,进一步加剧重复触发问题。
修复方案

按照以下步骤修改代码即可解决:

  1. 统一上下加载的触发逻辑,向上加载也使用fireRequestDistance作为阈值,同时新增内容高度判断避免初始误触发
  2. 插入头部数据后手动调整UICollectionView的contentOffset,保持可视位置不变的同时避免再次命中触发条件
  3. UICollectionView插入操作禁用动画,和UITableView行为保持一致
  4. 调整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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 16:57:04