Reputation: 1902
I have a number of SwapItem
structs, each with a child SwapItemChild
. Then, using a ForEach
of SwiftUI, I would like to display the name of each SwapItem
, called the item view, containing also a circle in the color of its respective SwapItemChild
, called the child view. Subsequently, I would like to swap the children of two items, and have the respective child views change places animated. This was inspired by other examples of this effect by this extensive tutorial, but not specifically the children view swapping.
I attempt to do so using a matchedGeometryEffect
identifying each child view by the id of the respective SwapItemChild
. However, this leads to a jumpy animation, where only the top child view moves down, whereas the bottom child view instantaneously jumps to the top.
The functional example code is as follows.
// MARK: - Model
struct SwapItem: Identifiable {
let id = UUID()
let name: String
var child: SwapItemChild
}
struct SwapItemChild: Identifiable {
let id = UUID()
let color: Color
}
class SwapItemStore: ObservableObject {
@Published private(set) var items = [SwapItem(name: "Task 1", child: SwapItemChild(color: .red)),
SwapItem(name: "Task 2", child: SwapItemChild(color: .orange))]
func swapOuterChildren(){
let tmpChild = items[0].child
items[0].child = items[1].child
items[1].child = tmpChild
}
}
// MARK: - View
struct SwapTestView: View {
@StateObject private var swapItemStore = SwapItemStore()
@Namespace private var SwapViewNS
var body: some View {
VStack(spacing: 50.0) {
Button(action: swapItemStore.swapOuterChildren){
Text("Swap outer children")
.font(.title)
}
VStack(spacing: 150.0) {
ForEach(swapItemStore.items){ item in
SwapTestItemView(item: item, ns: SwapViewNS)
}
}
}
}
}
struct SwapTestItemView: View {
let item: SwapItem
let ns: Namespace.ID
var body: some View {
HStack {
Circle()
.fill(item.child.color)
.frame(width: 100, height: 100)
.matchedGeometryEffect(id: item.child.id, in: ns)
.animation(.spring())
Text(item.name)
}
}
}
What is the correct implementation of matchedGeometryEffect
to have these child views swapping places seamlessly?
Upvotes: 4
Views: 1066
Reputation: 808
I have already encountered this kind of problem, try this :
ForEach(swapItemStore.items, id: \.self.child.id)
Another way :
struct SwapItem: Identifiable, Hashable {
let id = UUID()
let name: String
var child: SwapItemChild
}
struct SwapItemChild: Identifiable, Hashable {
let id = UUID()
let color: Color
}
with :
ForEach(swapItemStore.items, id: \.self)
See : https://www.hackingwithswift.com/books/ios-swiftui/why-does-self-work-for-foreach
Upvotes: 3