This happens as a result of the view that’s being dragged is faraway from the view hierarchy.
ForEach
makes use of its id:
parameter (if it has one) to trace when so as to add/take away views. The outer ForEach
creates two inside ForEach
es, one for every row. The inside ForEach
es makes use of array indices as their id
s. That is already a foul concept, given that you will re-order the inside arrays’ parts. See additionally: SwiftUI: Why does ForEach want an ID?
Initially, the IDs for the 2 rows are:
row 1: 0, 1, 2
row 2: 0, 1, 2
The final merchandise of the second row has 2 as its id. Whenever you transfer it, the ids now change into:
row 1: 0, 1, 2, 3
row 2: 0, 1
We see that the id similar to the final merchandise within the second row (i.e. 2) has disappeared from the second row. Because of this, ForEach
removes the view from the second row, cancelling the gesture. Then, the ForEach
for the primary row provides a brand new view to itself.
In the event you transfer a non-last merchandise, the identical change occurs, however that merchandise’s id won’t be faraway from the second row, so the gesture retains going.
As a proof of idea, should you use just one ForEach
, and a extra applicable id, such because the id
of the Merchandise
s, the gesture doesn’t get cancelled.
VStack {
Textual content("Dragging translation worth: n (translationHeight)")
LazyVGrid(columns: Array(repeating: .init(.versatile(minimal: 10, most: 50)), depend: 3)) {
let allIndices = array2d.indices.flatMap { i in
array2d[i].indices.map { j in (i, j) }
}
let _ = print("up to date")
let flattened = array2d.flatMap { $0 }
ForEach(Array(flattened.enumerated()), id: .aspect.id) { (i, elem) in
Textual content(elem.textual content)
.padding()
.background(Colour.inexperienced)
.gesture(
DragGesture(coordinateSpace: .world)
.onChanged({ worth in
translationHeight = worth.translation.top
if worth.translation.top > 200 && doItOnce {
let (j, ok) = allIndices[i]
let temp = array2d[j].take away(at: ok)
array2d[0].insert(temp, at: 0)
doItOnce = false
}
})
.onEnded({ worth in
doItOnce = true
translationHeight = 0
})
)
}
}
}