Swiftui 删除项目时列表的奇怪行为

Swiftui 删除项目时列表的奇怪行为,swiftui,Swiftui,删除项目时,我遇到一个奇怪的列表行为 数据模型 class Item: Identifiable, ObservableObject { let id = UUID() @Published var name: String internal init(name: String) { self.name = name } } 视图模型 class ListViewModel: ObservableObject { @Publis

删除项目时,我遇到一个奇怪的列表行为

数据模型

class Item: Identifiable, ObservableObject {
    let id = UUID()
    @Published var name: String
    
    internal init(name: String) {
        self.name = name
    }
}
视图模型

class ListViewModel: ObservableObject {
    @Published var items: [Item]
    
    internal init(items: [Item]) {
        self.items = items
    }

    internal init() {
        self.items = [Item(name: "A"), Item(name: "B"), Item(name: "C"), Item(name: "D")]
    }
}
看法

现在是奇怪的行为。点击一行的按钮:

  • 将从viewModel中删除右侧行
  • 该列表显示错误的项目,无论点击哪个删除按钮,都会删除最后一行
  • 我不知道我做错了什么

    更新

    显然,这个问题与ForEach有关,下面的代码可以正常工作

    struct ContentView: View {
        @ObservedObject var viewModel = ListViewModel()
    
        var body: some View {
    
            List(viewModel.items, id: \.id) { item in
                Row(item: item) { action in
                    switch action {
                    case .delete:
                        if let idx = viewModel.items.firstIndex(where: { $0.id == item.id }) {
                            viewModel.items.remove(at: idx)
                        }
                    case .edit:
                        break
                    default:
                        break
                    }
                }
            }
        }
    }
    
    

    我使用ForEach的方式是否错误?

    仅使用索引而不使用可识别值的
    ForEach
    会导致此类问题。如果可以,请始终使用唯一ID。
    struct ContentView: View {
        @ObservedObject var viewModel = ListViewModel()
    
        var body: some View {
            List {
                ForEach(0 ..< viewModel.items.count, id: \.self) { idx in
    
    //                HStack {
    //                    Text("\(viewModel.items[idx].name)")
    //                    Spacer()
    //                    Button("Edit") {
    //                        // To be done
    //                    }
    //                    .buttonStyle(BorderlessButtonStyle())
    //                    Button("Delete") {
    //                        viewModel.items.remove(at: idx)
    //                    }
    //                    .buttonStyle(BorderlessButtonStyle())
    //                }
    
                    Row(item: viewModel.items[idx]) { action in
                        switch action {
                        case .delete:
                            viewModel.items.remove(at: idx)
                        case .edit:
                            // To be done
                            break
                        default:
                            break
                        }
                    }
                }
            }
        }
    }
    
    
    
    enum ActionType {
        case new
        case edit
        case delete
        case cancel
    }
    
    struct Row: View {
        @State var item: Item
        var action: (_ type: ActionType) -> Void
    
        var body: some View {
            HStack {
                Text("\(item.name)")
                Spacer()
                Button("Edit") {
                    action(.edit)
                }
                .buttonStyle(BorderlessButtonStyle())
                Button("Delete") {
                    action(.delete)
                }
                .buttonStyle(BorderlessButtonStyle())
            }
        }
    }
    
    struct ContentView: View {
        @ObservedObject var viewModel = ListViewModel()
    
        var body: some View {
    
            List(viewModel.items, id: \.id) { item in
                Row(item: item) { action in
                    switch action {
                    case .delete:
                        if let idx = viewModel.items.firstIndex(where: { $0.id == item.id }) {
                            viewModel.items.remove(at: idx)
                        }
                    case .edit:
                        break
                    default:
                        break
                    }
                }
            }
        }
    }