在SwiftUI中使用ForEach与TextField

3
我正在尝试使用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,其他一些可能已经失效了。

非常感谢

2个回答

2

通过在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的bug,类似于this的问题。


很欣慰地得知这是一个bug。希望下个版本能修复它!当使用ListonDelete时已经可以正常工作了。 - undefined

1
我无法完美地解释是什么导致了崩溃,但我已经能够重现这个错误,看起来在删除一个字段后,SwiftUI 仍在寻找所有 indices ,当它尝试访问已删除索引处的元素时,无法找到它,从而导致 index out of bounds 错误。为了解决这个问题,我们可以编写一个条件语句,以确保只有在其 index 包含在 indices 集合中时才搜索 element
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)时不会搜索该元素。
这正是我所能理解的,但底层可能发生了其他事情。

这也是一个有效的答案!谢谢! - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接