SwiftUI中列表分两列后Delete Button失效问题排查与解决方案咨询
问题原因分析
你的Delete Button失效的核心原因在于List的编辑/删除机制和LazyVGrid的嵌套结构不兼容:
- List的
EditButton和onDelete是专门为List的直接子元素(也就是List的每一行)设计的。你把整个LazyVGrid作为List的一行,所以编辑模式下只会给这个"整行Grid"添加删除按钮,而不是Grid里的单个任务项。 - 你把
onDelete附加到了LazyVGrid上,而不是包裹任务的ForEach本身——就算移到ForEach上,因为它嵌套在Grid里,List的编辑系统也无法识别这些元素为可删除的独立项。
另外还有个小bug:你删除时用gp -= rate,但rate是当前输入的load1+load2,不是被删除任务对应的收入,这会导致gp计算错误。
解决方案:自定义编辑模式+ScrollView替代List
要保持两列布局同时恢复删除功能,我们可以移除外层的List,改用ScrollView包裹内容,自己实现编辑模式和删除逻辑,同时修复gp计算的问题。
步骤1:修正Task结构,存储对应收入
首先修改Task结构体,添加earning属性,这样删除时才能正确计算gp的减少:
struct Task : Identifiable { var id = String() var toDoItem = String() var earning: Float // 存储当前任务对应的收入值 }
步骤2:更新任务添加逻辑
修改addNewToDo函数,把当前的rate值存入新任务:
func addNewToDo() { let currentEarning = rate taskStore.tasks.append(Task( id: String(taskStore.tasks.count + 1), toDoItem: " Earning: = \(currentEarning.description)", earning: currentEarning )) }
步骤3:修复删除时的gp计算
更新delete函数,用被删除任务的earning来减少gp:
func delete(at offsets : IndexSet) { for offset in offsets { let task = taskStore.tasks[offset] gp -= task.earning } taskStore.tasks.remove(atOffsets: offsets) }
步骤4:重构布局,实现自定义编辑模式
把外层的List替换成ScrollView,添加自定义的编辑按钮,在编辑模式下显示每个任务的删除按钮:
struct Calculation: View { @State var load1 = Float() @State var load2 = Float() @State var gp : Float = 0 @State var rate: Float = 0 @ObservedObject var taskStore = TaskStore() @StateObject private var model = Model() @State private var isEditing = false // 控制编辑模式 func addNewToDo() { let currentEarning = rate taskStore.tasks.append(Task( id: String(taskStore.tasks.count + 1), toDoItem: " Earning: = \(currentEarning.description)", earning: currentEarning )) } var body: some View { NavigationView { ScrollView { VStack(spacing: 16) { // 模拟原来的Section header和输入框 VStack(alignment: .leading, spacing: 8) { Text("Load Inputs") .font(.headline) .padding(.horizontal) TextField("Enter value of load 1", value: $load1, format: .number) .textFieldStyle(.roundedBorder) .padding(.horizontal) TextField("Enter value of load 2", value: $load2, format: .number) .textFieldStyle(.roundedBorder) .padding(.horizontal) } HStack(spacing: 16) { Button("Add Load") { rate = load1 + load2 // 直接在这里计算rate,替代Rocky函数更直观 print("pay for the shift is \(rate)") gp += rate } .buttonStyle(.borderedProminent) Button("Add To List") { rate = load1 + load2 addNewToDo() } .buttonStyle(.bordered) } .padding(.horizontal) // 两列任务网格 LazyVGrid(columns: model.columns, spacing: 16) { ForEach(taskStore.tasks) { task in HStack { Text(task.toDoItem) .padding(12) .background(Color.gray.opacity(0.1)) .cornerRadius(8) // 编辑模式下显示删除按钮 if isEditing { Button(action: { if let index = taskStore.tasks.firstIndex(where: { $0.id == task.id }) { delete(at: IndexSet(integer: index)) } }) { Image(systemName: "trash") .foregroundColor(.red) } } } } } .padding(.horizontal) Text("Gross Pay = $\(String(format: "%.2f", gp)) ") .font(.title) .padding() } .padding(.vertical) } .navigationBarTitle("SHIFTS") .navigationBarItems(trailing: Button(isEditing ? "Done" : "Edit") { isEditing.toggle() }) } } func delete(at offsets : IndexSet) { for offset in offsets { let task = taskStore.tasks[offset] gp -= task.earning } taskStore.tasks.remove(atOffsets: offsets) } }
额外优化点
- 移除了冗余的
Rocky函数,直接在按钮动作里计算rate,代码更直观 - 修复了原来第二个TextField的placeholder错误(之前写的是"load 1")
- 给按钮添加了样式,提升UI可用性
- 用
String(format: "%.2f", gp)格式化gp显示,避免小数位数过多
这样修改后,你就能在两列布局下正常删除单个任务,同时gp的计算也会准确无误。
内容的提问来源于stack exchange,提问作者Ricky Chhetri
相关产品推荐
相关产品推荐

