4

I'm trying to animate a spinning "busy" image in a SwiftUI View using standard and consistent online tutorials. The image is square, should be scaled uniformly, and spin forever.

I can get a spinning image with the following code, however I am seeing unexpected translations with this. When used in a NavigationView as the initial View the image moves over the course of the animation from top-left to the center of the screen. When used as the third screen in a NavigationView the image starts roughly in the center of the screen and moves vertically downwards by about half its height over the course of the animation. Removing the navigationBar and back button reduce the vertical shift to about 20px overall. Which is interesting.

Also interestingly, the background stays exactly where I'd expect it to be, in the center of the screen in both cases. autoreverses behaves as expected and anchoring the rotation effect seems to have no effect.

The aim is to have additional controls on the screen (text, Next button etc) but for now I'd be happy with an animation that stays in one place.

What am I doing wrong? Any help appreciated!

Image illustrating the problemThe code:

@State private var isAnimating = false

var body: some View {
    VStack{
        Spacer()
        Image("FilmCircle")
            .resizable()
            .scaledToFit()
            .frame(width: 100, height: 100)
            .rotationEffect(Angle(degrees: isAnimating ? 360 : 0), anchor: .center)
            .animation(Animation.linear(duration: 2)
                .repeatForever(autoreverses: false))
            .onAppear {
                self.isAnimating = true
            }
            .background(Color.red)
        Spacer()
    }
}
Robin Macharg
  • 1,468
  • 14
  • 22
  • Does this answer your question https://stackoverflow.com/a/66643096/12299030? Or this one https://stackoverflow.com/a/64566746/12299030? – Asperi May 13 '22 at 10:47

1 Answers1

4

Declare .backgound(Color.red) before declaring animation so that it will animate background as well.

And for issue when using navigation view, see the code : -

    VStack{
    Spacer()
    Image("FilmCircle")
        .resizable()
        .scaledToFit()
        .background(Color.red)
        .frame(width: 100, height: 100)
        .rotationEffect(Angle(degrees: isAnimating ? 360 : 0), anchor: .center)
        .onAppear {
             DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
                           withAnimation(.linear(duration: 2).repeatForever(autoreverses: false)) {
                               isAnimating = true
                           }
                       }
        }
        Spacer()
    }

Hope you found this helpful.

Namra Parmar
  • 227
  • 2
  • 6
  • 1
    @Asperi's second link in their comment got me on the right track and the code you provided mirrors what I now have working. As you point out, having a `DispatchQueue` call containing the `withAnimation` call to start animation is the way to go, forcing layout to happen before animation starts. The red background was for debugging, so not requiring to be animated, but the point about its position was also useful. Thanks! – Robin Macharg May 13 '22 at 11:29