如何在动态列表中创建按钮修改布尔值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
相关产品推荐
相关产品推荐

