UICollectionView未滚动却自动触发分页请求的原因是什么?
问题触发原因
- 触发逻辑的位置选择错误:
collectionView(_:cellForItemAt:)是系统为指定indexPath准备cell时就会调用的代理方法,不是cell真正展示到屏幕上才触发。UICollectionView默认开启预加载机制,会提前加载可视区域附近的cell,哪怕你没有手动滚动页面,初始渲染时如果最后一个cell被纳入预加载范围,就会命中判断触发请求。 - 缺少分页状态判断:你已经定义了
isPaginating标记位,但触发请求前没有判断该状态,就算已经在请求下一页数据的过程中,只要命中最后一行的判断就会重复发起请求,加剧异常触发的概率。 - 额外的潜在问题:你在请求回调中直接使用了
sleep(2),如果该回调在主队列执行,会直接阻塞主线程导致UI卡顿,同时也会拉长isPaginating的重置周期,更容易出现逻辑异常。
优化方案
- 更换触发逻辑的位置:将分页触发逻辑放到
collectionView(_:willDisplay:forItemAt:)方法中,该方法是cell即将显示到屏幕上时才会调用,判断时机比cellForItemAt更准确。 - 增加多重校验:触发请求前先判断
isPaginating是否为false,以及是否还有更多数据可以加载,避免重复无效请求。
优化后示例代码:
// 移除原cellForItemAt中的分页逻辑,替换为willDisplay代理实现 override func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { // 同时满足是最后一行、不在分页中、还有更多数据可加载三个条件才触发请求 guard indexPath.row == dataGroup.data.count - 1, !isPaginating, hasMoreData else { // hasMoreData为你维护的是否存在下一页的标记 return } isPaginating = true Service.shared.fetchData(page: self.page) { [weak self] rawData, error in guard let self = self else { return } if let error = error { print("error while fetching app groups", error) self.isPaginating = false return } // 测试阶段需要模拟延迟不要用sleep,可改为在后台队列延迟发起请求 if let data = rawData { self.dataGroup?.data += data.data DispatchQueue.main.async { self.collectionView.reloadData() } self.page += 1 // 接口返回数据为空时标记无更多数据,停止后续分页请求 self.hasMoreData = !data.data.isEmpty } self.isPaginating = false } }
内容的提问来源于stack exchange,提问作者someone
相关产品推荐
相关产品推荐

