Swift中为加载JSON数据的UICollectionView添加分页功能的实现方法
分页功能失效原因及修复方案
现有代码存在的问题
- 数据请求后直接覆盖原有数据源,没有把新页数据追加到现有列表里
loadMoreData方法里没有实际发起新的接口请求,只做了延迟和空刷新- 滚动触发分页的阈值逻辑不合理,不需要乘4,应该是接近底部时就触发
- 没有实现底部加载指示器,也没有无更多数据的终止判断
- 分页参数逻辑需要确认后端要求:通常分页需要两个参数:
pageSize(每页条数,固定为10)和pageIndex(页码,从0/1开始累加),或者offset(偏移量,已加载的总条数)+limit(每页条数),你当前只传count累加10,需要先确认后端的参数规则。
如果你的后端接口逻辑是传入
count参数返回对应数量的全量数据(比如count=10返回前10条,count=20返回前20条),那你不需要追加数据,直接保留原有替换逻辑即可,不用改下面的数据源追加部分,同时参数count还是传currentPageNumberVM即可。
修复步骤
1. 新增必要属性
// 每页固定加载10条 let pageSize = 10 // 是否还有更多数据可加载,防止接口返回空后还继续请求 var hasMoreData = true // 底部加载指示器 let footerViewActivityIndicator = UIActivityIndicatorView(style: .medium)
2. 修改接口请求逻辑,区分首次加载和加载更多
func serviceCall(isLoadMore: Bool = false){ // 首次加载/下拉刷新重置偏移量,加载更多再累加偏移量 if isLoadMore { self.currentPageNumberVM += pageSize } else { self.currentPageNumberVM = 0 hasMoreData = true } // 这里根据后端要求调整参数,示例为通用的offset+limit分页参数 let param = [ "jsonrpc": "2.0", "params": [ "type" : type, "limit": pageSize, "offset": currentPageNumberVM ] ] as [String : Any] isLoading = true footerViewActivityIndicator.startAnimating() APIReqeustManager.sharedInstance.serviceCall(param: param, vc: self, url: getUrl(of: .productByFeature), header: header) {(responseData) in self.isLoading = false self.footerViewActivityIndicator.stopAnimating() if responseData.error != nil{ self.view.makeToast(NSLocalizedString("Something went wrong!", comment: "")) // 加载失败回滚偏移量 if isLoadMore { self.currentPageNumberVM -= self.pageSize } }else{ self.viewmoreDB = ViewMoreBase(dictionary: responseData.dict as NSDictionary? ?? NSDictionary()) let newProducts = self.viewmoreDB?.result?.products ?? [] if isLoadMore { // 加载更多就把新数据追加到原有列表 self.productsData?.append(contentsOf: newProducts) } else { // 首次加载/下拉刷新直接替换列表 self.productsData = newProducts } // 新返回的数据量小于页大小,说明没有更多数据了 self.hasMoreData = newProducts.count >= self.pageSize self.collectionView.reloadData() } } }
3. 修改滚动触发逻辑
func scrollViewDidScroll(_ scrollView: UIScrollView) { // 正在加载、没有更多数据时不触发加载更多 guard !isLoading, hasMoreData else { return } let offsetY = scrollView.contentOffset.y let contentHeight = scrollView.contentSize.height let scrollViewHeight = scrollView.frame.size.height // 滚动到距离底部不足100px时触发加载更多,阈值可自行调整 if offsetY > 0, offsetY + scrollViewHeight >= contentHeight - 100 { loadMoreData() } }
4. 修改loadMoreData方法,实际发起加载更多请求
func loadMoreData() { guard !isLoading, hasMoreData else { return } serviceCall(isLoadMore: true) }
5. 实现底部加载指示器
在viewDidLoad中注册CollectionView的底部Footer:
collectionView.register(UICollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "FooterView")
实现Footer的相关数据源方法:
extension 你的控制器类名: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize { // 只有还有更多数据时才显示Footer高度 return hasMoreData ? CGSize(width: collectionView.frame.width, height: 50) : .zero } func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { if kind == UICollectionView.elementKindSectionFooter { let footerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "FooterView", for: indexPath) footerView.addSubview(footerViewActivityIndicator) footerViewActivityIndicator.center = footerView.center return footerView } return UICollectionReusableView() } }
6. 首次加载调用
页面首次加载时调用serviceCall()即可自动加载第一页10条数据。
内容的提问来源于stack exchange,提问作者learn_swift
相关产品推荐
相关产品推荐

