First here's a block of code that reproduce the issue:
import SwiftUI
struct CustomAnimation: ViewModifier {
@State private var isAnimated: Bool = false
func body(content: Content) -> some View {
content
.phaseAnimator([true, false], trigger: self.isAnimated) { view, phase in
view
.scaleEffect(phase ? 1 : 1.2)
}
.onTapGesture {
self.isAnimated.toggle()
}
}
}
#Preview {
@Previewable @State var isPresented: Bool = true
VStack {
Button {
isPresented = true
} label: {
Text("show sheet")
}
}
.sheet(isPresented: $isPresented) {
VStack {
Spacer()
Text("my animated text")
.modifier(CustomAnimation())
}
}
}
The issue is that the animated text, placed on the bottom of the screen with a spacer, jitter up (and goes back to its place) when I drag the sheet up, after the view has been animated at least once.
The issue is visible only if the animated view is sticked to the bottom of the screen (eg. with a spacer), and only after an animation.
I've tried to debug with instruments hitches but I'm new to that one and don't understand much how to get informations.
The issue appears when building on a device but also in previews. I've noticed on device if I put the app in background it's like the animation didn't take place and the issue disappear, until I trigger an other animation.
It is easier to see that the jitter is somehow connected with the sheet height when you set medium detents:
.sheet(isPresented: $isPresented) {
VStack {
// ...
}
.presentationDetents([.medium]) // 👈 here
}
It helps to add .geometryGroup()
after the modifier .phaseAnimator
:
content
.phaseAnimator([true, false], trigger: isAnimated) { view, phase in
view
.scaleEffect(phase ? 1 : 1.2)
}
.onTapGesture {
isAnimated.toggle()
}
.geometryGroup() // 👈 here
This seems to "anchor" the text, like on first show.