I have a View that displays several identical items and when certain events occur a decorator is placed next to one of the items. E.g. The items are footballers and the decorator is a ball. The View was getting quite complicated so I decomposed the view into a hierarchy of subviews.
Now I’d like to use matchedGeometryEffect to create a movement animation for the decorator. E.g. The ball slides from one footballer to another.
This works well when all the items and the decorator are in one container view. The ball slides from one footballer to another. But I can’t figure how to get this to work when the decorator is in a subview. I could refactor to bring the decorator (e.g. ball) back to the main View, but that would make the code quite messy. Is there another way to achieve this?
One big view – works
struct Kick: View {
@State var isFlipped = false
@Namespace var animation
var body: some View {
VStack {
Button("Kick") {
withAnimation {
isFlipped.toggle()
}
}
HStack {
Spacer()
if isFlipped {
HStack {
ZStack {
Rectangle()
.fill(.red)
Rectangle()
.fill(Color.clear)
.strokeBorder(Color.black, lineWidth: 5)
.matchedGeometryEffect(id: "ball", in: animation)
} .frame(width: 50, height: 50)
Spacer()
Rectangle()
.fill(.blue)
.frame(width: 50, height: 50)
}
} else {
HStack {
Rectangle()
.fill(.red)
.frame(width: 50, height: 50)
Spacer()
ZStack {
Rectangle()
.fill(.blue)
Rectangle()
.fill(Color.clear)
.strokeBorder(Color.black, lineWidth: 5)
.matchedGeometryEffect(id: "ball", in: animation)
} .frame(width: 50, height: 50)
}
}
}
}
}
}
Using subviews – does not work (no sliding transition, just fading)
struct Kick2: View {
@State var isFlipped = false
var body: some View {
VStack {
Button("Kick") {
withAnimation {
isFlipped.toggle()
}
}
HStack {
Footballer(color: Color.red, kicked: isFlipped)
Spacer()
Footballer(color: Color.blue, kicked: !isFlipped)
}
}
}
}
struct Footballer: View {
var color: Color
var kicked: Bool
@Namespace var animation
var body: some View {
ZStack {
Rectangle()
.fill(color)
if kicked {
Rectangle()
.fill(Color.clear)
.strokeBorder(Color.black, lineWidth: 5)
.matchedGeometryEffect(id: "ball", in: animation)
}
} .frame(width: 50, height: 50)
}
}