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

如何在动态列表中创建按钮修改布尔值isTaken状态?

SwiftUI动态列表添加按钮修改Medicine的isTaken属性

首先要确保Medicine符合Identifiable协议,方便ForEach遍历列表元素,下面分两种常见类型给出实现方案:

情况1:Medicine为值类型(struct)

先定义Medicine结构:

struct Medicine: Identifiable {
    let id = UUID() // 自动生成唯一标识
    var title: String
    var description: String
    var date: Date
    var isTaken: Bool
    
    // 可选:封装修改逻辑的方法
    mutating func markAsTaken() {
        isTaken = true
    }
}

ViewModel实现:

class MedicineViewModel: ObservableObject {
    @Published var storedMedicines: [Medicine] = [
        Medicine(title: "Medicine 1", description: "Lower blood sugar", date: .init(timeIntervalSince1970: 1664724035), isTaken: false),
        Medicine(title: "Medicine 1", description: "Lower blood sugar", date: .init(timeIntervalSince1970: 1664720930), isTaken: false),
        Medicine(title: "Medicine 1", description: "Lower blood sugar", date: .init(timeIntervalSince1970: 1664720990), isTaken: false),
        Medicine(title: "Medicine 1", description: "Lower blood sugar", date: .init(timeIntervalSince1970: 1664720990), isTaken: false),
    ]
    
    // 可选:通过ID标记已服用的方法
    func markMedicineAsTaken(by id: UUID) {
        guard let index = storedMedicines.firstIndex(where: { $0.id == id }) else { return }
        storedMedicines[index].isTaken = true
        // 值类型修改后需重新赋值,触发@Published更新视图
        storedMedicines = storedMedicines
    }
}

列表视图实现:

struct MedicineListView: View {
    @StateObject private var viewModel = MedicineViewModel()
    
    var body: some View {
        List {
            // 使用$绑定数组元素,直接修改属性会自动触发视图更新
            ForEach($viewModel.storedMedicines) { $medicine in
                HStack {
                    VStack(alignment: .leading) {
                        Text(medicine.title)
                        Text(medicine.description)
                            .font(.caption)
                        Text(medicine.date.formatted())
                            .font(.caption)
                    }
                    Spacer()
                    Button(action: {
                        // 方式1:直接通过绑定修改
                        medicine.isTaken = true
                        // 方式2:调用viewModel的方法
                        // viewModel.markMedicineAsTaken(by: medicine.id)
                    }) {
                        Text(medicine.isTaken ? "已服用" : "标记已服用")
                            .foregroundColor(.white)
                            .padding(.horizontal, 16)
                            .padding(.vertical, 8)
                            .background(medicine.isTaken ? .gray : .blue)
                            .cornerRadius(8)
                    }
                    .disabled(medicine.isTaken) // 已服用后禁用按钮,防止重复操作
                }
            }
        }
    }
}

情况2:Medicine为引用类型(class)

定义Medicine类:

class Medicine: ObservableObject, Identifiable {
    let id = UUID()
    var title: String
    var description: String
    var date: Date
    @Published var isTaken: Bool // 用@Published标记属性,修改时自动通知视图更新
    
    init(title: String, description: String, date: Date, isTaken: Bool) {
        self.title = title
        self.description = description
        self.date = date
        self.isTaken = isTaken
    }
    
    // 封装修改逻辑
    func markAsTaken() {
        isTaken = true
    }
}

ViewModel实现:

class MedicineViewModel: ObservableObject {
    @Published var storedMedicines: [Medicine] = [
        Medicine(title: "Medicine 1", description: "Lower blood sugar", date: .init(timeIntervalSince1970: 1664724035), isTaken: false),
        Medicine(title: "Medicine 1", description: "Lower blood sugar", date: .init(timeIntervalSince1970: 1664720930), isTaken: false),
        Medicine(title: "Medicine 1", description: "Lower blood sugar", date: .init(timeIntervalSince1970: 1664720990), isTaken: false),
        Medicine(title: "Medicine 1", description: "Lower blood sugar", date: .init(timeIntervalSince1970: 1664720990), isTaken: false),
    ]
}

列表视图实现:

struct MedicineListView: View {
    @StateObject private var viewModel = MedicineViewModel()
    
    var body: some View {
        List {
            ForEach(viewModel.storedMedicines) { medicine in
                HStack {
                    VStack(alignment: .leading) {
                        Text(medicine.title)
                        Text(medicine.description)
                            .font(.caption)
                        Text(medicine.date.formatted())
                            .font(.caption)
                    }
                    Spacer()
                    Button(action: {
                        medicine.markAsTaken()
                    }) {
                        Text(medicine.isTaken ? "已服用" : "标记已服用")
                            .foregroundColor(.white)
                            .padding(.horizontal, 16)
                            .padding(.vertical, 8)
                            .background(medicine.isTaken ? .gray : .blue)
                            .cornerRadius(8)
                    }
                    .disabled(medicine.isTaken)
                }
                .environmentObject(medicine) // 确保视图能响应medicine内部@Published属性的变化
            }
        }
    }
}

关键说明

  • 值类型(struct):修改属性时,要么通过ForEach($array)的绑定直接修改,要么修改后重新赋值数组触发@Published更新。
  • 引用类型(class):只要属性用@Published标记,修改时视图会自动刷新,无需额外操作数组。
  • 添加按钮禁用状态,提升用户体验,避免重复标记。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 00:35:19