SwiftUI Property Animation: Why They’re Not Working and How to Fix Them

Property animation is the most common type of animation used in SwiftUI. It involves animating changes to a specific property of a view, such as its position, scale, or opacity. To create a property animation in SwiftUI, you use the animation(_:value:) modifier, which takes an animation object as its parameter.

Single Property Animation

So, if you’re reading this, chances are you’ve already tried your hand at doing a single property animation in SwiftUI. And you probably found it to be fairly easy to pull off, right? If that’s the case, your code probably looked something like this:

struct ContentView: View {
    @State private var scale = 1.0
    
    var body: some View {
        VStack() {
            Circle()
                .frame(width: 200, height: 200)
                .padding()
                .foregroundColor(.purple)
                .scaleEffect(scale)
                .animation(.easeInOut(duration: 0.5), value: scale)

            Button("Animate") {
                scale = 1.2
            }
            .padding()
        }.padding()
    }
}

So, what’s going on in this code? Essentially, it’s just animating the scale of a circle.

How does it do that, you ask? Well, the animation(_:value:) function on line 11 is observing changes to the scale property on line 2 and applying an easeInOut(duration:) animation on line 12.

And where’s the actual change to the scale property happening, you might wonder? That’s taking place on the Animate button action on line 14. Easy peasy, right?

Alright, now let’s see what happens when we run this code, shall we? Here’s the result!

SwiftUI property animation

Multi Property Animation

So, you’ve got your single property animation working like a charm, and now you’re itching to animate another property of the same view. Makes sense, right? After all, why stop at just one animation when you can have multiple?

So, let’s say we’ve already got our circle’s scale animation sorted out. What’s the next thing we might want to tackle? How about changing the circle’s color from purple to yellow? Feeling encouraged by our previous success, we might be tempted to modify our code in a similar way…

struct ContentView: View {
    @State private var scale = 1.0
    @State private var circleColor = Color.purple
    
    var body: some View {
        VStack() {
            Circle()
                .frame(width: 200, height: 200)
                .padding()
                .foregroundColor(circleColor)
                .scaleEffect(scale)
                .animation(.easeInOut(duration: 0.5), value: scale)
                .animation(.easeIn(duration: 3.0), value: circleColor)

            Button("Animate") {
                scale = 1.2
                circleColor = .yellow
            }
            .padding()
        }.padding()
    }
}

We’ve introduced a new property called circleColor, which we want to observe and animate as well. And how do we make that happen? Simple – we just add some code on line 13 and we end up with this …

SwiftUI property animation

However, when we run this new code, we notice that things aren’t working quite as they should be. Sure, the circle’s color is getting animated, but it’s using the animation defined in the scale property animation (both scale and color animations have the same duration which is not what we defined on line 13). And that’s definitely not what we were going for here…

How To Fix This?

Making this code work is very easy! All we need to do is adjust the order of the animations being called. Take a look at the code below:

struct ContentView: View {
    @State private var scale = 1.0
    @State private var circleColor = Color.purple
    
    var body: some View {
        VStack() {
            Circle()
                .frame(width: 200, height: 200)
                .padding()
                .foregroundColor(circleColor)
                .animation(.easeIn(duration: 3.0), value: circleColor)
                .scaleEffect(scale)
                .animation(.easeInOut(duration: 0.5), value: scale)

            Button("Animate") {
                scale  = 1.2
                circleColor = .yellow
            }
            .padding()
        }.padding()
    }
}

We can achieve the desired final result simply by calling the animation after applying the property (lines 11 and 13). Check it out:

SwiftUI property animation

Leave a Reply

Your email address will not be published. Required fields are marked *