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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.18 23:45:03