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.
Workaround (I found a solution, see below)
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)
}
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
}
}
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…