TableView自定义分页:willDisplayCell触发非可视单元格问题求解
解决UITableView分页时willDisplay提前触发的问题
我完全懂你遇到的这个痛点——UITableView为了让滚动更流畅,会提前预加载一些不在当前屏幕内的单元格,结果就是willDisplay甚至cellForRowAt会在最后一个单元格还没进入用户视野时就触发加载请求,完全不符合我们想要的"滚动到底部才加载"的分页逻辑。下面给你两个实用的解决方案,附带代码调整:
方案一:优化willDisplay触发逻辑,确保单元格真正可见
核心思路是在原有判断基础上,新增单元格是否处于屏幕可视范围的校验,同时加上加载状态锁,彻底避免重复请求。
首先添加一个加载状态标记:
private var isLoading = false // 标记是否正在加载数据,防止重复请求
然后修改你的willDisplay方法:
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { // 正在加载时直接返回,避免重复触发请求 guard !isLoading else { return } // 先判断是否是最后一行,且还有未加载的数据 guard indexPath.row == orders.count - 1, totalItems > orders.count else { return } // 校验当前单元格是否真的进入了屏幕可视区域 let cellFrame = tableView.rectForRow(at: indexPath) let visibleFrame = CGRect(origin: tableView.contentOffset, size: tableView.bounds.size) guard cellFrame.intersects(visibleFrame) else { return } // 满足所有条件,触发下一页加载 fetchNextPage() }
接着更新fetchNextPage和addOrders方法,维护加载状态:
private func fetchNextPage() { isLoading = true currentPage += 1 delegate?.didReachLastCell(page: currentPage) } func addOrders(_ orders: [OrderView]) { self.orders.append(contentsOf: orders) reloadOrders() // 数据加载完成后,重置加载状态 isLoading = false }
方案二:改用scrollViewDidScroll监听滚动,自定义加载时机
这种方式更灵活,无需依赖单元格的预加载逻辑,而是主动监听滚动位置,当用户滚动到距离底部一定距离时触发加载(比如提前100pt开始加载,让用户几乎感知不到等待)。
同样保留isLoading状态锁,实现UIScrollViewDelegate的方法:
override func scrollViewDidScroll(_ scrollView: UIScrollView) { // 过滤掉加载中、没有更多数据的情况 guard !isLoading, totalItems > orders.count else { return } let contentOffsetY = scrollView.contentOffset.y let contentHeight = scrollView.contentSize.height let tableViewHeight = scrollView.bounds.size.height let triggerThreshold: CGFloat = 100 // 提前加载的距离,可根据需求调整 // 计算是否滚动到了触发加载的位置 if contentOffsetY > contentHeight - tableViewHeight - triggerThreshold { fetchNextPage() } }
关键注意事项
- 加载状态锁是核心:不管用哪种方案,
isLoading都能有效防止滚动过程中多次触发请求,避免重复数据或服务器报错。 - 保证totalItems的准确性:这个值必须是服务器返回的真实总数据条数,否则会出现无限加载或提前停止加载的问题。
- 处理contentInset:如果你的tableView设置了
contentInset(比如底部有安全区),计算滚动位置时要把contentInset.bottom纳入考量,避免触发时机不准确。
内容的提问来源于stack exchange,提问作者atereshkov
相关产品推荐
相关产品推荐

