SwiftUI从JSON数据源加载数据时如何实现Load more功能
最优实现方案
针对JSON数据源的分页加载场景,推荐采用「ViewModel统一管理状态+自动+手动双触发加载」的方案,和你现有代码的兼容性最高,逻辑也最清晰:
1. 新建分页管理的ViewModel
所有分页参数、JSON请求逻辑、数据存储都放到ViewModel中,和UI层解耦:
import SwiftUI // 你自己定义的Recommended数据模型,要遵守Identifiable、Codable协议 struct Recommended: Identifiable, Codable { let id: Int // 其他字段按需补充 } class RecommendedViewModel: ObservableObject { @Published var recommendeds: [Recommended] = [] @Published var isLoading = false // 是否正在请求中,避免重复请求 @Published var hasMoreData = true // 是否还有更多数据可加载 private var currentPage = 1 // 当前页码,首次请求默认第1页 private let pageSize = 10 // 每页返回数量,按需和接口对齐 // 分页请求JSON数据的核心方法 func fetchNextPage() { // 正在加载或者已经没有更多数据时直接拦截 guard !isLoading && hasMoreData else { return } isLoading = true // 替换为你自己的JSON接口地址,拼接分页参数 guard let url = URL(string: "你的接口请求地址?page=\(currentPage)&pageSize=\(pageSize)") else { isLoading = false return } URLSession.shared.dataTask(with: url) { [weak self] data, response, error in guard let self = self, let data = data, error == nil else { DispatchQueue.main.async { self?.isLoading = false } return } do { // 解析JSON返回的当前页数据 let newPageList = try JSONDecoder().decode([Recommended].self, from: data) DispatchQueue.main.async { // 新数据拼到已有数组尾部 self.recommendeds.append(contentsOf: newPageList) self.currentPage += 1 // 如果返回的数量小于页大小,说明已经到最后一页 self.hasMoreData = newPageList.count >= self.pageSize self.isLoading = false } } catch { DispatchQueue.main.async { self.isLoading = false } print("JSON解析失败: \(error)") } }.resume() } // 点击Show All一键加载全部的方法,按需实现 func fetchAll() { // 重置分页状态,重新拉取所有数据 currentPage = 1 recommendeds.removeAll() hasMoreData = true fetchNextPage() } }
2. 改造现有UI代码
保留你原有布局结构,仅增加加载触发逻辑和底部状态提示:
// 当前视图中注入ViewModel @StateObject private var vm = RecommendedViewModel() Group { HStack { Text("Recommended Events") .font(.title3) .foregroundColor(.white) .fontWeight(.bold) Spacer() Button(action: { vm.fetchAll() }) { Text("Show all") .font(.title3) .foregroundColor(Color.white) .fontWeight(.bold) } } .padding(15) ForEach(vm.recommendeds) { recommended in NavigationLink(destination: RecommendedEventsDetailView(recommended: recommended)) { RecommendedEventsView(recommended: recommended) } // 滚动到最后一项时自动触发下一页加载 .onAppear { if recommended.id == vm.recommendeds.last?.id { vm.fetchNextPage() } } } // 底部状态提示 if vm.isLoading { ProgressView() .tint(.white) .padding(.vertical, 12) } else if !vm.hasMoreData && !vm.recommendeds.isEmpty { Text("已加载全部内容") .foregroundColor(.gray) .padding(.vertical, 12) } } .onAppear { // 页面首次加载时拉取第一页数据 if vm.recommendeds.isEmpty { vm.fetchNextPage() } }
方案优势
- 逻辑分层清晰,和你现有代码结构无冲突,不需要大幅重构
- 自动滚动加载+手动一键加载两种方式结合,覆盖不同使用场景
- 内置重复请求拦截、无更多数据判断,避免接口资源浪费
内容的提问来源于stack exchange,提问作者EMPIRE
相关产品推荐
相关产品推荐

