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

SwiftUI中如何展示列表中多选项目的合并详情视图

SwiftUI 列表多选及批量详情加载实现方案

核心思路

通过ObservableObject搭配@Published统一管理选中项状态,借助ViewModel封装API调用逻辑,最终实现批量展示所有选中项的详情数据。

1. 创建ViewModel管理状态与API逻辑

import SwiftUI

// 定义详情数据模型(根据实际API返回结构调整)
struct FruitDetail: Codable, Identifiable {
    var id: String { name }
    let name: String
    let nutrition: String
    let origin: String
    // 其他需要展示的字段...
}

class FruitViewModel: ObservableObject {
    @Published var items: [String] = ["Apples", "Oranges", "Bananas", "Pears", "Mangos","grapefruit","Kela","Angur","Papita","tarbooj"]
    @Published var selectedFruits: [String] = []
    @Published var fruitDetails: [FruitDetail] = []
    @Published var isLoading: Bool = false
    @Published var errorMessage: String?
    
    // 批量加载选中水果的详情API
    func loadSelectedDetails() async {
        isLoading = true
        errorMessage = nil
        fruitDetails.removeAll()
        
        var fetchedDetails: [FruitDetail] = []
        for fruit in selectedFruits {
            do {
                // 替换为你的详情API地址
                guard let url = URL(string: "https://your-api.com/fruits/\(fruit.lowercased())") else {
                    throw NSError(domain: "无效URL", code: -1, userInfo: nil)
                }
                let (data, _) = try await URLSession.shared.data(from: url)
                let detail = try JSONDecoder().decode(FruitDetail.self, from: data)
                fetchedDetails.append(detail)
            } catch {
                errorMessage = "加载\(fruit)详情失败: \(error.localizedDescription)"
            }
        }
        
        DispatchQueue.main.async {
            self.fruitDetails = fetchedDetails
            self.isLoading = false
        }
    }
    
    // 切换水果选中状态
    func toggleSelection(for fruit: String) {
        if selectedFruits.contains(fruit) {
            selectedFruits.removeAll { $0 == fruit }
        } else {
            selectedFruits.append(fruit)
        }
    }
}

2. 修改主视图绑定ViewModel

struct HomeActivity: View {
    @StateObject private var viewModel = FruitViewModel()
    @State private var showDetailView = false
    
    var body: some View {
        NavigationView {
            VStack {
                Button("查看选中项详情") {
                    showDetailView = true
                }
                .disabled(viewModel.selectedFruits.isEmpty)
                .padding()
                
                List {
                    ForEach(viewModel.items, id: \.self) { fruit in
                        DetailView(
                            title: fruit,
                            isSelected: viewModel.selectedFruits.contains(fruit)
                        ) {
                            viewModel.toggleSelection(for: fruit)
                        }
                    }
                }
            }
            .navigationTitle("水果列表")
            .sheet(isPresented: $showDetailView) {
                SelectedItemsDetailView(viewModel: viewModel)
            }
            .alert(item: $viewModel.errorMessage) { message in
                Alert(title: Text("加载失败"), message: Text(message), dismissButton: .default(Text("确定")))
            }
        }
    }
}

// 保留原选中项视图,简化逻辑
struct DetailView: View {
    var title: String
    var isSelected: Bool
    var action: () -> Void
    
    var body: some View {
        Button(action: action) {
            HStack {
                Text(title)
                if isSelected {
                    Spacer()
                    Image(systemName: "checkmark")
                }
            }
        }
    }
}

3. 创建批量详情展示视图

struct SelectedItemsDetailView: View {
    @ObservedObject var viewModel: FruitViewModel
    
    var body: some View {
        NavigationView {
            VStack {
                if viewModel.isLoading {
                    ProgressView("加载中...")
                } else if viewModel.fruitDetails.isEmpty {
                    Text("暂无详情数据")
                        .foregroundColor(.gray)
                } else {
                    List(viewModel.fruitDetails) { detail in
                        VStack(alignment: .leading, spacing: 8) {
                            Text(detail.name)
                                .font(.headline)
                            Text("营养信息: \(detail.nutrition)")
                            Text("产地: \(detail.origin)")
                        }
                        .padding(.vertical, 4)
                    }
                }
            }
            .navigationTitle("选中水果详情")
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button("刷新") {
                        Task {
                            await viewModel.loadSelectedDetails()
                        }
                    }
                }
            }
            .onAppear {
                Task {
                    await viewModel.loadSelectedDetails()
                }
            }
        }
    }
}

关键说明

  • 状态同步:用@StateObject和@Published实现选中项的响应式更新,确保所有视图实时同步选中状态。
  • API批量调用:通过async/await实现异步请求,遍历选中项逐个调用详情API,完成后统一更新UI。
  • 错误处理:通过@Published的errorMessage传递错误信息,用系统弹窗展示给用户。
  • 视图交互:主视图通过sheet弹出详情页,详情页在加载时显示进度条,支持手动刷新数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 11:45:57