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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 18:49:05