ios开发:下拉刷新和上拉加载更多

一,接口代码

image

二,swift代码

viewmodel

import Foundation
import Alamofire

@MainActor // 确保 UI 更新在主线程
class PoemViewModel: ObservableObject {
    
    @Published var poems: [Poem] = []
    @Published var isLoading = false
    @Published var currentPage = 0
    @Published var isFull = false       // 是否全部加载完毕
    
    func fetchData(destPage: Int,isRefresh: Bool=false) async {
        
        // 如果正在加载或已加载完毕且不是刷新,则直接返回
        if self.isLoading || (self.isFull && !isRefresh) { return }
        
        if isRefresh {
            self.isFull = false
        }
        
        self.isLoading = true
        //let nextPage = currentPage+1
        let url = "http://www.test.net/list.php?p="+String(destPage)
        print("当前请求接口:"+url)
        // 使用泛型解析
        let result = await AF.request(url).serializingDecodable(ApiResponse<PoemListData>.self).result
        switch result {
        case .success(let response):
            if (isRefresh == true) {
                self.poems = response.data.list
            } else {
                self.poems.append(contentsOf: response.data.list)
            }
            if response.data.pageinfo.current_page == response.data.pageinfo.pages {
                print("isFull = true")
                self.isFull = true
            } else {
                print("isFull = false")
            }
            
            self.currentPage = destPage
        case .failure(let error):
            print("请求失败:\(error)")
        }
        self.isLoading = false
    }
}

view

import SwiftUI
import Alamofire


struct ContentView: View {
    @State private var message = "加载中..."

        //@State private var poems: [Poem] = [] // 存储解析后的数组
        // 初始化 ViewModel
        @StateObject private var viewModel = PoemViewModel()
    
         var body: some View {

             NavigationStack {
                 VStack {
                     Text(message)

                     List {
                         if viewModel.isLoading && viewModel.poems.isEmpty {
                             ProgressView("加载中..")
                         }
                         ForEach(viewModel.poems){ poem in
                             NavigationLink(value: poem) {
                                  PoemRow(poem: poem) // 进一步拆分出子视图
                                     .onAppear() {
                                         
                                         // 核心逻辑:如果当前行是数组最后一行,触发加载更多
                                         if poem == viewModel.poems.last {
                                             Task {
                                                 let nextPage = viewModel.currentPage+1
                                                 await viewModel.fetchData(destPage:nextPage,isRefresh: false)
                                             }
                                         }
                                         /*
                                         print("appear:"+poem.title)
                                         if viewModel.poems.count >= 3 && poem == viewModel.poems[viewModel.poems.count - 3] {
                                             Task {
                                                 let nextPage = viewModel.currentPage+1
                                                 await viewModel.fetchData(destPage:nextPage,isRefresh: false)
                                             }
                                         }
                                          */
                                     }
                             }
                         }
                     }
                     .navigationTitle("唐诗鉴赏1")
                     .navigationDestination(for: Poem.self) { poem in
                         Mp3View()
                     }
                     // 调用逻辑
                     .task{
                         print("列表出现时的task")
                         await viewModel.fetchData(destPage:1,isRefresh: false)
                     }
                     .refreshable{
                         await viewModel.fetchData(destPage:1,isRefresh: true)
                     }
                     
                 }
             }
         }

    
    
}


// 一行元素的view
struct PoemRow: View {
    let poem: Poem
    var body: some View {
        HStack(spacing: 15) {
            AsyncImage(url: URL(string: poem.image)) { image in
                image.resizable().aspectRatio(contentMode: .fit)
            } placeholder: {
                Color.purple.opacity(0.1)
            }
            .frame(width: 60, height: 60)
            .background(Color.purple)
            .cornerRadius(8)
            
            VStack(alignment: .leading){
                Text(poem.title).font(.headline)
                Text(poem.author).font(.subheadline).foregroundColor(.secondary)
            }
        }.frame(height:120)
    }
}

#Preview {
    ContentView()
}

model

import Foundation

// 使用泛型 T,并约束 T 必须符合 Decodable 协议
struct ApiResponse<T: Decodable>: Decodable {
    let status: String
    let code: Int
    let time: String
    let msg: String
    let data: T  // 这里可以是任何类型:List 结构体、单个对象、甚至是一个数组
}

// 针对你当前接口的具体数据结构
struct PoemListData: Decodable {
    let list: [Poem]
    let pageinfo: PageInfo
}

// 使用泛型 T,并约束 T 必须符合 Decodable 协议
struct PageInfo: Decodable {
    let pages: Int
    let current_page: Int
}

struct Poem: Decodable, Identifiable, Hashable {
    let id: Int
    let author: String
    let title: String
    let image: String
}

三,测试效果:

image

 

posted @ 2026-04-06 10:05  刘宏缔的架构森林  阅读(1)  评论(0)    收藏  举报