【问题标题】:Delete a Binding from a list in SwiftUI从 SwiftUI 中的列表中删除绑定
【发布时间】:2021-10-28 02:13:49
【问题描述】:

我试图从 Swift 和 SwiftUI 的列表中简单地删除一个元素。如果没有在ForEach 循环中绑定某些东西,它确实会被删除。但是,在绑定某些东西时,它会因错误Index out of range 而崩溃。 ForEach 循环似乎是恒定的,不更新,并试图在特定索引处呈现。

示例查看代码:

@ObservedObject var todoViewModel: TodoViewModel
//...
ForEach(self.todoViewModel.todos.indices) { index in
    TextField("Test", text: self.$todoViewModel.todos[index].title)
        .contextMenu(ContextMenu(menuItems: {
            VStack {
                Button(action: {
                    self.todoViewModel.deleteAt(index)
                }, label: {
                    Label("Delete", systemImage: "trash")
                })
            }
        }))                                    
}

示例视图模型代码:

final class TodoViewModel: ObservableObject {
    @Published var todos: [Todo] = []
    
    func deleteAt(_ index: Int) -> Void {
        self.todos.remove(at: index)
    }
}

示例型号代码:

struct Todo: Identifiable {
    var id: Int
    var title: String = ""
}

有谁知道如何从ForEach 循环中绑定的列表中正确删除一个元素?

【问题讨论】:

  • 这是否回答了您的问题stackoverflow.com/a/58911168/12299030
  • 遗憾的是没有。我也注意到了这一点,但是您可以在代码中看到我没有使用 ForEach 中的任何范围

标签: arrays swift swiftui swift3


【解决方案1】:

发生这种情况是因为您通过indices 进行枚举并通过ForEach 内部的索引引用绑定

我建议你切换到ForEachIndexed:这个包装器会将索引和正确的绑定传递给你的块:

struct ForEachIndexed<Data: MutableCollection&RandomAccessCollection, RowContent: View, ID: Hashable>: View, DynamicViewContent where Data.Index : Hashable
{
    var data: [(Data.Index, Data.Element)] {
        forEach.data
    }
    
    let forEach: ForEach<[(Data.Index, Data.Element)], ID, RowContent>
    
    init(_ data: Binding<Data>,
         @ViewBuilder rowContent: @escaping (Data.Index, Binding<Data.Element>) -> RowContent
    ) where Data.Element: Identifiable, Data.Element.ID == ID {
        forEach = ForEach(
            Array(zip(data.wrappedValue.indices, data.wrappedValue)),
            id: \.1.id
        ) { i, _ in
            rowContent(i, Binding(get: { data.wrappedValue[i] }, set: { data.wrappedValue[i] = $0 }))
        }
    }
    
    init(_ data: Binding<Data>,
         id: KeyPath<Data.Element, ID>,
         @ViewBuilder rowContent: @escaping (Data.Index, Binding<Data.Element>) -> RowContent
    ) {
        forEach = ForEach(
            Array(zip(data.wrappedValue.indices, data.wrappedValue)),
            id: (\.1 as KeyPath<(Data.Index, Data.Element), Data.Element>).appending(path: id)
        ) { i, _ in
            rowContent(i, Binding(get: { data.wrappedValue[i] }, set: { data.wrappedValue[i] = $0 }))
        }
    }
    
    var body: some View {
        forEach
    }
}

用法:

ForEachIndexed($todoViewModel.todos) { index, todoBinding in
    TextField("Test", text: todoBinding.title)
        .contextMenu(ContextMenu(menuItems: {
            VStack {
                Button(action: {
                    self.todoViewModel.deleteAt(index)
                }, label: {
                    Label("Delete", systemImage: "trash")
                })
            }
        }))
}

【讨论】:

  • 效果非常好!感谢您的出色工作!
猜你喜欢
  • 2021-02-07
  • 1970-01-01
  • 2020-05-08
  • 1970-01-01
  • 1970-01-01
  • 2021-08-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多