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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.15 03:24:06