Search code examples
animationswiftui

Animate scale of view back to initial state when done?


I have this simple view:

struct ContentView: View {
    @State private var scale:Double = 1
    
    var body: some View {
        Rectangle()
            .fill(.red)
            .frame(width: 200, height: 200)
            .scaleEffect(scale)
            .animation(Animation.easeInOut(duration: 1).repeatCount(4, autoreverses: true).delay(1), value: scale)
            .onAppear {
                scale = 1.2
            }
    }
}

It produces this animation:

enter image description here

You'll notice at the end of the animation it snaps to scale 1.2 since that is the value of the state scale effect follows.

I am want to trigger the scale animation on appear, and make it "pulse" 4 times from state 1 to 1.2 and back, 4 times. Right now it does start at scale 1, pulses, but at the end it snaps to 1.2. How can I fix this so it creates a "pulse" animation that starts at 1, scales to 1.2 and back 4 times?


Solution

  • For future reference, in iOS 17, the withAnimation() function now includes a completion callback that executes when the animation finishes.

    Given the previous scenario, you should return the view to its initial state when the animation finishes. Since you want four repetitions, you need to subtract one repetition from the completion callback, as it will be called after the animation has already completed once. Here's the modified code:

    struct ContentView: View {
        @State private var scale: Double = 1
    
        var body: some View {
            Rectangle()
                .fill(.red)
                .frame(width: 200, height: 200)
                .scaleEffect(scale)
                .onAppear {
                    withAnimation(Animation.easeInOut(duration: 1).repeatCount(3, autoreverses: true).delay(1)) {
                        scale = 1.2
                    } completion: {
                        withAnimation(Animation.easeInOut(duration: 1)) {
                            scale = 1
                        }
                    }
                }
        }
    }
    

    Outcome:

    enter image description here