【问题标题】:Using TextField with ForEach in SwiftUI在 SwiftUI 中将 TextField 与 ForEach 一起使用
【发布时间】:2021-05-15 14:50:53
【问题描述】:

我正在尝试使用ForEach 显示文本字段的动态列表。以下代码按预期工作:我可以添加/删除文本字段,并且绑定是正确的。但是,当我在 ObservableObject 视图模型中移动 items 时,它不再工作,并因 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,另一些则不工作(不再?)。

非常感谢

【问题讨论】:

    标签: swiftui swiftui-foreach


    【解决方案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时,不会搜索该元素。

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

    【讨论】:

    • 这也是一个有效的答案!谢谢!
    【解决方案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 错误,例如与this 类似的问题。

    【讨论】:

    • 得知这是一个错误,我松了一口气。希望下个版本能修复!使用List 和onDelete 时已经可以使用。
    猜你喜欢
    • 2020-04-17
    • 2020-04-05
    • 2020-06-20
    • 1970-01-01
    • 1970-01-01
    • 2014-05-21
    • 2022-01-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多