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

Swift如何使用cellWillDisplay/scrollViewDidScroll实现API数据分页加载

解决方案

你使用的台北市开放数据API原生支持服务端分页参数,可直接通过添加limit和offset参数实现分页加载,性能优于本地拆分数据的方案,修改步骤如下:


1. 新增分页控制属性

在你的ViewController中添加以下属性:

// 单页加载条数
private let pageSize = 20
// 当前已加载数据的偏移量
private var currentOffset = 0
// 全部数据总条数,用于判断是否还有下一页
private var totalCount = 0
// 防重复加载标记
private var isLoading = false
// 已加载的展示数据列表
private var listOfBotanical: [ResultsDetail] = []

2. 调整Model层请求方法

修改请求方法支持传入分页参数,同时返回总条数用于分页判断:

func getBotanicalResults(offset: Int, limit: Int, completionHandler: @escaping (Result<(total: Int, list: [ResultsDetail]), BotanicalError>) -> Void) {
    // 拼接分页参数到请求URL
    let urlString = "https://data.taipei/opendata/datalist/apiAccess?scope=resourceAquire&rid=f18de02f-b6c9-47c0-8cda-50efad621c14&limit=\(limit)&offset=\(offset)"
    
    guard let url = URL(string: urlString) else {
        completionHandler(.failure(.invalidURL))
        return
    }
    let decoder = JSONDecoder()
    URLSession.shared.dataTask(with: url) { data, response, error in
        guard let jsonData = data else {
            completionHandler(.failure(.noDataAvailable))
            return
        }
        do {
            let botanicalResponse = try decoder.decode(BotanicalMain.self, from: jsonData)
            // 需确保你的BotanicalMain模型中包含result下的总条数字段count
            let total = botanicalResponse.result.count
            let list = botanicalResponse.result.results
            completionHandler(.success((total, list)))
        } catch {
            completionHandler(.failure(.canNotProcessData))
        }
    }.resume()
}

3. 重构fetchData方法支持分页逻辑

func fetchData(isLoadMore: Bool = false) {
    guard !isLoading else { return }
    isLoading = true
    
    let requestOffset = isLoadMore ? currentOffset : 0
    model.getBotanicalResults(offset: requestOffset, limit: pageSize) { [weak self] result in
        guard let self = self else { return }
        self.isLoading = false
        
        switch result {
        case .failure(let error):
            print(error)
        case .success(let response):
            self.totalCount = response.total
            if isLoadMore {
                self.listOfBotanical.append(contentsOf: response.list)
            } else {
                self.listOfBotanical = response.list
            }
            self.currentOffset = self.listOfBotanical.count
            
            DispatchQueue.main.async {
                self.tableView.reloadData()
            }
        }
    }
}

首次加载直接调用fetchData()即可,不需要传参。

4. 实现滚动到底部自动加载下一页

二选一即可:

方案A:通过scrollViewDidScroll判断

override func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let offsetY = scrollView.contentOffset.y
    let contentHeight = scrollView.contentSize.height
    let frameHeight = scrollView.frame.size.height
    
    // 滚动到距离底部100px时触发加载,阈值可自行调整
    if offsetY > contentHeight - frameHeight - 100, currentOffset < totalCount {
        fetchData(isLoadMore: true)
    }
}

方案B:通过willDisplayCell判断

override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    // 显示到最后一个cell时触发加载
    if indexPath.row == listOfBotanical.count - 1, currentOffset < totalCount {
        fetchData(isLoadMore: true)
    }
}

本地分页方案(无需修改请求逻辑)

如果你不想修改请求逻辑,也可以首次请求拿到全部220条数据后存在本地全量数组,每次加载更多时从全量数组中截取20条拼到展示数组中,逻辑和上述流程类似,仅不需要多次发起网络请求。

内容的提问来源于stack exchange,提问作者Krauser Huang

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 23:06:06