SwiftUI: Animate changes in List without animating content changes
The following should disable animations for row internals
VStack(alignment: .leading) {
Text(item.name)
Text(self.distanceString(for: item.distance))
}
.animation(nil)
The answer by @Asperi fixed the issue I was having also (Upvoted his answer as always).
I had an issue where I was animating the whole screen in using the below: AnyTransition.asymmetric(insertion: .move(edge: .bottom), removal: .move(edge: .top))
And all the Text() and Button() sub views where also animating in weird and not so wonderful ways. I used animation(nil) to fix the issue after seeing Asperi's answer. However the issue was that my Buttons no longer animated on selection, along with other animations I wanted.
So I added a new State variable to turn on and off the animations of the VStack. They are off by default and after the view has been animated on screen I enable them after a small delay:
struct QuestionView : View {
@State private var allowAnimations : Bool = false
var body : some View {
VStack(alignment: .leading, spacing: 6.0) {
Text("Some Text")
Button(action: {}, label:Text("A Button")
}
.animation(self.allowAnimations ? .default : nil)
.onAppear() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
self.allowAnimations = true
}
}
}
}
Just adding this for anyone who has a similar issue to me and needed to build on Asperi's excellent answer.
Thanks to @Brett for the delay solution. My code needed it in several places, so I wrapped it up in a ViewModifier.
Just add .delayedAnimation() to your view.
You can pass parameters for defaults other than one second and the default animation.
import SwiftUI
struct DelayedAnimation: ViewModifier {
var delay: Double
var animation: Animation
@State private var animating = false
func delayAnimation() {
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
self.animating = true
}
}
func body(content: Content) -> some View {
content
.animation(animating ? animation : nil)
.onAppear(perform: delayAnimation)
}
}
extension View {
func delayedAnimation(delay: Double = 1.0, animation: Animation = .default) -> some View {
self.modifier(DelayedAnimation(delay: delay, animation: animation))
}
}