I have a ForEach
block and a Stepper
embedded in a List
view. The contents of the List
view\'s first section is as follow
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.
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)
}
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)
}
}
I had a same problem i solve with adding if condition before ForEach.
if (!IsDeleting) {
...
.OnDecrement{
IsDeleting := true
}
}
any better solution?
It works as the following codes.
struct Node {
var xString: String = "x"
var yString: String = "y"
var x: Int
var y: Int
}
struct RecordsNode {
var nodes : [Node]
}
struct ContentView: View {
@State var record: RecordsNode = RecordsNode(nodes: [Node(x: 11, y: 11)])
var body: some View {
Group{
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()
})}
}}