从 SwiftUI 中的列表中删除绑定
Delete a Binding from a list in SwiftUI
我想简单地从 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
循环中的列表中正确删除元素?
发生这种情况是因为您通过 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] = [=10=] }))
}
}
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] = [=10=] }))
}
}
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")
})
}
}))
}
我想简单地从 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
循环中的列表中正确删除元素?
发生这种情况是因为您通过 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] = [=10=] }))
}
}
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] = [=10=] }))
}
}
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")
})
}
}))
}