I have a component view that shows a button conditionally. I want this button to animate while appearing/disappearing. .transition(.move(edge: .trailing))
does what I want.
However, if the parent of the component moves, the animation of the child runs relative to the global frame, not relative to its parent's frame.
I believe there should be a way to modify this behavior. The component implementation should not depend on how it is used.
I have an alternative approach to achieve what I want, but it’s not a transition animation. It modifies the offset manually. However, I believe there should be a solution that uses transitions. The child of the component may require the appear/disappear lifecycle for other purposes.
import SwiftUI
import PlaygroundSupport
struct Component: View {
let state: Bool
var body: some View {
HStack() {
Text("Placeholder")
.padding()
Spacer()
if (state) {
Color.red
.frame(width: 20, height: 20)
.padding()
.transition(.move(edge: .trailing))
}
}
.frame(width: 200, height: 200)
.background(Color.blue)
.clipped()
}
}
struct ComponentAlternative: View {
let state: Bool
var body: some View {
HStack() {
Text("Placeholder")
.padding()
Spacer()
Color.red
.frame(width: 20, height: 20)
.padding()
.transition(.move(edge: .trailing))
.offset(x: state ? 0 : 50)
}
.frame(width: 200, height: 200)
.background(Color.blue)
.clipped()
}
}
struct DemoView: View {
@State private var state: Bool = false
var body: some View {
ZStack {
Toggle(isOn: $state, label: {})
Component(state: state)
.frame(width: 200, height: 200)
.background(Color.blue)
.clipped()
.padding(.top, state ? 200 : 0)
}
.frame(width: 400, height: 400)
.animation(.default.speed(0.1), value: state)
}
}
PlaygroundPage.current.setLiveView(DemoView())
This is what geometryGroup
is for. Add it to ComponentView
, but before .padding(.top, state ? 200 : 0)
.
Component(state: state)
.geometryGroup() // <-----
.frame(width: 200, height: 200)
.background(Color.blue)
.clipped()
.padding(.top, state ? 200 : 0)
This essentially prevents the padding animation from affecting the Component
directly. The parent animates its padding, and the Color.red
animates the transition, all separately.