在 SwiftUI 中将 TextField 与 ForEach 一起使用

问题描述

我正在尝试使用 ForEach 显示文本字段的动态列表。以下代码按预期工作:我可以添加/删除文本字段,并且绑定正确。但是,当我在 items 视图模型中移动 ObservableObject 时,它不再工作,并因 index out of bounds 错误而崩溃。这是为什么?我怎样才能让它工作?

struct ContentView: View {
    @State var items = ["A","B","C"]
    
    var body: some View {
        vstack {
            ForEach(items.indices,id: \.self) { index in
                FieldView(value: Binding<String>(get: {
                    items[index]
                },set: { newValue in
                    items[index] = newValue
                })) {
                    items.remove(at: index)
                }
            }
            Button("Add") {
                items.append("")
            }
        }
    }
}

struct FieldView: View {
    @Binding var value: String
    let onDelete: () -> Void
    
    var body: some View {
        HStack {
            TextField("item",text: $value)
            Button(action: {
                onDelete()
            },label: {
                Image(systemName: "multiply")
            })
        }
    }
}

我尝试使用的视图模型:

class viewmodel: Observable {
    @Published var items: [String]
}
@Observedobject var viewmodel: viewmodel

我发现很多问题都涉及相同的问题,但我无法解决我的案例。其中一些没有提到 TextField,其他一些不起作用(不再起作用?)。

非常感谢

解决方法

通过检查 Binding 内的边界,您可以解决问题:

struct ContentView: View {
    @ObservedObject var viewModel: ViewModel = ViewModel(items: ["A","B","C"])
    
    var body: some View {
        VStack {
            ForEach(viewModel.items.indices,id: \.self) { index in
                FieldView(value: Binding<String>(get: {
                    guard index < viewModel.items.count else { return "" } // <- HERE
                    return viewModel.items[index]
                },set: { newValue in
                    viewModel.items[index] = newValue
                })) {
                    viewModel.items.remove(at: index)
                }
            }
            Button("Add") {
                viewModel.items.append("")
            }
        }
    }
}

这是一个 SwiftUI 错误,例如类似于 this 的问题。

,

我无法完美解释导致崩溃的原因,但我已经能够重现该错误,并且在删除字段后,SwiftUI 仍在寻找所有 indices 以及何时它试图访问已删除索引处的元素,但无法找到它,从而导致 index out of bounds 错误。

为了解决这个问题,我们可以编写一个条件语句来确保仅当 element 包含在 index 的集合中时才搜索 indices

FieldView(value: Binding<String>(get: {
    if viewModel.items.indices.contains(index) {
        return viewModel.items[index]
    } else {
        return ""
    }
},set: { newValue in
    viewModel.items[index] = newValue
})) {
    viewModel.items.remove(at: index)
}

上面的解决方案解决了这个问题,因为它确保当元素数(items.count)不大于index时不会搜索元素。

这只是我能够理解的,但在幕后可能还有其他事情发生。