删除行时SwiftUI ForEach索引超出范围错误 [英] SwiftUI ForEach index out of range error when removing row
问题描述
我有一个 ForEach
块和一个 Stepper
嵌入在 List
视图中.List
视图第一部分的内容如下:
I have a ForEach
block and a Stepper
embedded in a List
view. The contents of the List
view's first section is as follows:
ForEach(record.nodes.indices, id: .self) { index in
HStack {
TextField("X", text: self.$record.nodes[index].xString)
Spacer()
Divider()
TextField("Y", text: self.$record.nodes[index].yString)
Spacer()
}
}
Stepper("± node", onIncrement: {
self.record.nodes.append(Node(x: 0, y: 0))
}, onDecrement: {
self.record.nodes.removeLast()
})
我面临的问题是,在调用 self.record.nodes.removeLast()
时,应用程序因 Index out of range
错误而崩溃.我一直试图解决这个问题几个小时,但无济于事.
The issue I am facing is that upon calling self.record.nodes.removeLast()
, the application crashes with an Index out of range
error. I've been trying to solve this for hours, but to no avail.
我最初使用了 onDelete
,但是这产生了同样的问题.
I originally used onDelete
, however that produced the same issue.
该项目可以在 https://github.com/jacobcxdev/Timekeeper 找到,带有这个Record.swift.
The project can be found at https://github.com/jacobcxdev/Timekeeper, with this error happening in RecordDetailView.swift.
推荐答案
问题描述
索引超出范围错误是因为init(_ data: Range
ForEach
的初始化器不允许我们动态修改数组.为此,我们需要使用 init(_ data: Data, content: @escaping (Data.Element) -> Content)
初始值设定项,如 评论.但是,正如@JacobCXDev 在回复中澄清的那样,它在嵌套绑定的这种情况下也不起作用.
Problem Description
The index out of range error occurs because init(_ data: Range<Int>, content: @escaping (Int) -> Content)
initializer of ForEach
does not allow us to modify the array dynamically. For that, we need to use init(_ data: Data, content: @escaping (Data.Element) -> Content)
initializer as @Asperi explained in the comment. However, it does not work either in this situation, where bindings are nested, as @JacobCXDev clarified in the reply.
使用自定义绑定和附加状态.自定义绑定解决了嵌套绑定上的 ForEach
问题.最重要的是,您需要在每次触摸自定义绑定后修改状态以重新渲染屏幕.以下是一个简化的(未经测试的)代码示例.
Use custom bindings and an additional state. The custom bindings solve the issue of ForEach
over nested bindings. On top of that, you need to modify a state after every touch on the custom binding to re-render the screen. The following is a simplified (untested) code sample.
@State private var xString: String
ForEach(record.nodes) { node in
let xStringBinding = Binding(
get: { node.xString },
set: {
node.xString = $0
self.xString = $0
}
)
TextField("X", text: xStringBinding)
}
解决方案(2020 年 7 月 10 日添加)
只需为子项定义一个视图结构,如下所示.
Solution (added on July 10, 2020)
Just define a view struct for the children like the following.
ForEach(record.nodes) { node in
NodeView(node: node)
}
struct NodeView: View {
@ObservedObject var node: Node
var body: some View {
TextField("X", text: self.$node.xString)
}
}
class Node: ObservableObject, Identifiable {
@Published var xString: String
let id: String = UUID().uuidString
init(xString: String) {
self.xString = xString
}
}
这篇关于删除行时SwiftUI ForEach索引超出范围错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!