I experienced rendering problems when using animations inside a NavigationView. The same animations work fine when no NavigationView is present.
See my example code below. There are two previews, one working, the other one not. Unfortunately, this behavior is not limited to previews but also happens when the app runs in simulator or on a real device.
Am I doing something wrong? Any help would be appreciated. Thanks!
import SwiftUI
struct MyView: View {
@State private var isAnimating = false
var body: some View {
let animation = Animation
.linear
.repeatForever(autoreverses: false)
return Image(systemName: "iphone")
.rotationEffect(.degrees(isAnimating ? 360 : 0))
.animation(animation)
.onAppear { isAnimating = true }
}
}
struct MyView_Previews: PreviewProvider {
static var previews: some View {
Group {
MyView()
.previewDisplayName("Working")
NavigationView {
MyView()
}
.previewDisplayName("Not Working")
}
}
}
(Xcode12.5, iOS14.5)
After 3 months I finally found a solution which I would like to share with you. There are two steps you need to take to get it to work:
- Make the animation explicit. That means: use
.withAnimation{}
instead of.animation()
. - Dispatch to main queue (
DispatchQueue.main.async{}
) before calling.withAnimation
. I have no idea why this is needed.
There is an article by javier on swift-lab called "Advanced SwiftUI animations" that helped me a lot. (Unfortunately the forum doesn't allow me to add a link.)
Here is the working result:
import SwiftUI
struct ContentView: View {
@State private var isAnimating = false
var body: some View {
let animation = Animation
.linear
.repeatForever(autoreverses: false)
return Image(systemName: "iphone")
.rotationEffect(.degrees(isAnimating ? 360 : 0))
.onAppear {
DispatchQueue.main.async {
withAnimation(animation) {
isAnimating = true
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
Group {
ContentView()
.previewDisplayName("Working")
NavigationView {
ContentView()
}
.previewDisplayName("Now also working")
}
}
}