Demo
So when we take a look at animations, there’s multiple ways to do animation within SwiftUI. One of the easiest ways is to use what’s called implicit animation. This is animation that takes place via a view modifier called animation. And what this does is there’s a state property that is being monitored for change. And when that value is changed, the change in the value is animated.
Like in this case here, we have an on tap gesture. So when we click tap me, we’re gonna change the scale by adding 1.0 to it when it starts out at one. And what this will do, using the animation modifier, is usually the ease in ease out animation, which is a timing curve, and it performs the animation to make the scale larger.
So we’ll demonstrate. So if we see each time we tap it, we’re increasing the scale by 1.0, and we get this slow animation where it’s getting larger.
Next, we’ll take a quick look at explicit animation. With explicit animation, this provides us much more control over how and when the animations are executed. For example, when we looked in our last example, the implicit animation, whenever that property changed, the animation would take place with explicit animation. This animation occurs inside a with animation block, not this is different than the case of implicit, which worked with an animation modifier.
With explicit animations, we can animate multiple state changes at a time, unlike implicit, but only works off one specific state. So here, what we see here is the same type of code. We’ve got a text button that says, tap me. And in the on tap gesture we have on line 12, we have the width animation. So we’re using the same type of animation, the ease and ease out timing curve.
And we’re changing the rotation by 45. In this case it would be 45 degrees. And here we have the rotation effect modifier, which is where we tell it the rotation by the degrees. And again, the width animation is going to animate that change. So when we click on it, we see that we’re gonna rotate it 45 degrees each time. And the change from zero to 45, and then from 45 to 90 is animated on each click. And this is what we can do with explicit animations.
It is possible to combine both implicit and explicit animations on the same view. As we see in this example here, we have our text field that’s with tap me and a button that will animate the scale and rotation. When we click the tap me text, when we click the button, the implicit animation in this example is again, the animation modifier, which we’re using the ease in out timing curve.
And the button is using the with animation to animate the change of the rotation. So here we see that in this case, we’re able to get both the implicit and the explicit animations to work at the same time.
Even though implicit animations only work of one specific state property, you can have multiple implicit animations on the same view. For each implicit animation, you’re going to need a state property that will be monitored for the change, and you’ll need an animation modifier for that. So if we take a look at this view here we have our animate meet text field. And in this we have an implicit animation for the opacity, and we have an implicit animation for the scale effect. And finally, we have an implicit animation for the visibility.
And what we’ll see here is that our first animation will be the visibility. So we will fade it in or fade out. The second animation is we’re gonna do the scaled, which will animate the change of the scale value. And then finally we have our third animation, which is going to be a spring effect, which is going to animate the scale.
And we can see each of these individually by clicking their individual button. So for toggle visibility, we can see it slowly fades in and fades back out. Our toggle the scale gets a little bit bigger, and then we click it again, it reverts back. And then finally we can see all three at one time. And there we have a little bit more complex animation, all done with implicit animations.
One of the things to keep in mind is that implicit animations are good for single property animations, where the default behavior is fine. Like in this case here, the animation of the change of value. Explicit animations work are better for complex animations of multiple property changes and scenarios requiring precise control over the animation.
When we went over implicit and explicit animations, I mentioned timing curves, in those previous examples, you’ve seen a few of them. But first, let’s talk about what timing curves are. A timing curve defines the speed of an animation over time, and it controls the pacing of the animation, for example, such as a slow start, fast end.
These become very important because they provide natural and smooth animations, and they make your animations more engaging to the user. SwiftUI has several built in timing curves that you can use. Some of them we’ve already seen, but let’s go over all of them.
The first is ease in. The ease in animation starts slow and speeds up. The ease out animation, which we see in the second one, starts fast and slows down. So it’s just the opposite of the ease in.
The ease in out animation starts slow, speeds up and then slows down again. So we got start slow, speeds up, slows down again. And then we also have linear. With linear, this is just a constant speed. The start and finish are at the exact same speed.
And then we finally have the timing curve itself, which allows us to give a custom animation so we can customize how we want this to start.
Now this view shows, and also too, before we go and look in the view, we can combine these timing curves together. So like in our bottom one here at the bottom, we will see that we have an ease in out and an ease in that takes place on there along with two implicit animation changes as well that we see on the rotation and the scale effect. So let’s first take a look. Now when I click this, they’re all going to change.
But let’s take a look first here at this top blue button, this is the ease in animation. And this one starts slow and speeds up. The second one, which is our red circle, this is an ease in ease out animation. And this one starts fast, but slows down. And then we take a look at the green one. We have an ease in ease out. Now we’ve seen this in our implicit and explicit animations demos, but this demo here starts slow, speeds up and then starts slow again. So we take a look at the green button, see a little bit of a speed up there in the middle.
And now we take a look at linear animation. When we, again, with linear, this is a constant speed. It’s start and stop are at the same speed and with no change. And now we take a look at the timing curve animation in this. Here we show exactly what our speed is going to be and how we want this to move. And in the timeframe of two seconds. And this is a standard bezier curve that we’re using. And then when we look at our last one, the blue, which is our custom timing curves, you notice this one just has an animation effect of getting larger and smaller. It’s just a little like a slow peek to it.
Let’s take a little more look into the animation dot timing curve, which is where we can build our own custom timing curves. In this example, we’re going to show a moving diagonal timing curve, a move horizontal, and a kind of playful corkscrew timing curve. You’ll note when we look at the code from these, that all three of these animations are implicit animations. We know this because we are using a single state property and the animation modifier.
So for our first one, we take a look at this red circle here, and in this one we just want this to move diagonal, and we kind of want it to follow the same kind of ease and ease out where it starts slow, speeds up and then slows down at the end. The next one we wanna look at is the horizontal. And this one is this green cube is going to move down or up depending on where it stayed whenever we clicked on it. And it too will use an ease in ease out type of curve.
And then finally we’ll take a look at this blue, which will follow a corkscrew path, which is kind of interesting. I don’t see where we would use this in much UI, but it does kind of show you the power of what you can do with these timing curves.
Now if you notice all these timing curves, they have different sets of parameters. And these parameters dictate how the base curve is going to execute. At the top here, I’ve included a link to cubic-bezier.com, which is a little web app that you can play around with to start with the basic ease, ease in, ease out linear type curves, and then modify those to get the timing parameters that you can use inside your apps.
Next we’ll take a look at phase animation. This is a new view and view modifier that was introduced in iOS 17. And what the phase animation does is it allows developers to define multiple stages of an animation via an array. And the animation seamlessly loops between these stages creating smooth transitions.
As we’ll see in this little demo here, we have an enumeration called animation phase, which is a type double and CaseIterable. There’s three cases. We have fade in, middle, and zooming out, and we’ve also assigned a raw value to these: zero, one and three. This is so we can use these in the scale effect and opacity properties.
And what we see here on line 14 is we see the modifier and it’s phase animator, and we’re gonna use the animation phase. We’re gonna loop through all of the cases of animation phase, starting with fade in, middle, and then zooming out. And in here we’re gonna pass the content. In this case here is this text view that we’re using here on line 12 and the phase that we’re currently in.
So we see here on line 15 the content, we’re gonna apply the scale effect to the phase, to the raw value of the phase, which the first iteration is zero. Second iteration is one, and third iteration is three. And we’ll do the same thing with the opacity, where we will check to see if it is equal to one. If it is, then we’re gonna leave it at one. And if it’s not one, we’re gonna set it to zero.
And what we see here in our preview is the animation, the text comes in, starts to zoom out, gets bigger with the scale and then it zooms out through its opacity. So this is where we look at zero for the fade in for the scale effect. Then the middle is one, so we’re given the scale effect of one. And then the zoom out, we’re animating the scale effect from one to three, which gives us the zoom out.
In this demo, we’re going to take a look at the phase animator view. And this animation’s gonna be a little bit more complex because we’re gonna have many more different phases. And everything that happens between those phases is dependent upon what the value of the phase is. So we start off first with defining our enumeration phase, which is going to give us the phases of initial, expanded, rotated, color and final.
The enumeration also provides computed properties for duration and animation, which will set the duration and the animation for each phase. And we’re going to use this inside of our view called multi-step animation view. And in here on line 51, we see that we’re defining our phase animator view. And in this case here we’re telling it what phases we’re gonna use. In this case, we’re gonna go through, iterate through all cases.
And in this case, unlike the last view where it was just continually running forever, this is going to work off a trigger. And we’re going to use the state property isAnimating as a way to start this animation. And that is controlled by our start animation button. And we have a reset button.
While this looks fairly concise, we got our rounded rectangle here, and we see it’s corner radius is defined by its phase. The color is defined by the phase, the width and height is defined. Rotation effect, all these are defined by the phase. So depending on what phase we’re in, we’re going to see a much different view inside this.
And we can see this through when we take a look at our internal functions that we have for this, such as size, color, rotation, scale, and corner radius. These all return the value based upon the phase that we’re in. And remember, just like the first example with the phase animator, we’re gonna iterate through all of the phases.
So we’re gonna start here at our first phase, which is our initial, and go all the way through to the final phase whenever we click the animation button. And we can see here when we click it, these are the animations that we get in one piece. So this is where some of the things we can see the control and the complexity that we can get inside the phase animation.
Finally, what we’re gonna look at now is the key frame animator. The key frame animator gives us the most control over our animations inside SwiftUI. And it’s very similar to the phase animation example, but there are a few little key differences and we’ll go over those as we take a look at this code.
One of the things we first wanna take a look at is what are we going to animate in this case here? What are the things we want to do? In the case of the phase animator, we define that as an enum. But with the key frame animator, we need to use a struct. The reason for that is in the phase animator we’re looking at discrete animation sets, whereas in the key frame animator we’re looking at continuous values here.
So with a key frame animator, SwiftUI needs to be able to calculate intermediate values. So like for example, if we were to take the scale from 1.0 to 2.0, Swift needs to be able to calculate 25% of that move, 50% of that move, 75% move all the way up to 2.0%. And that’s so we can mathematically interpolate between start and end. Whereas with the phase animation, we were just using discrete states.
So here, while it looks the same, we need to use a struct and we can animate anything that implements the Animatable protocol. That’s something else that’s pretty important to note is that we can animate the change of just about any object as long as it implements the Animatable protocol. All the numeric values in Swift do implement the Animatable protocol. So our animation values struct is ready to go.
So now if we take a look up here at our view, we wanna start here at line 10 ‘cause this is where we’re defining our key frame animator view. This looks a little similar to the phase animator. The difference though is that the first parameter we’re given an initial value, unlike the phase animation, which used a set of values like an array or something like that. The key frame animator is looking for the initial value.
In this case, we just need to send it an implementation of, or excuse me, an object of animation values. And with all of our properties set to defaults, we just simply put the constructor, that’s it. We can also have a trigger. And in this case we do have a trigger and our trigger’s gonna be based off the private state of trigger. And it’s set to false. This is controlled by the little animate bell button.
And that’s exactly what we’re going to do. We’re going to animate the movement of this bell, and through that we’re going to animate the rotation, the scale, and the offset. And the hope is to kind of give this bell a thing where it kind of comes out towards us, does a little bit of a spin around. And also gives that little ring bit and then comes back to its original state.
So here, you’ve seen some of this before. We’re gonna have the value in, we’re not passing the content like we did last time because we’re gonna have our content right inside here ‘cause that’s what the closure is going to return. And our content is our text and then we define our key frames.
And one of the things we have that’s a little bit different here is you notice that we have this little underscore here, which you remember. That’s kind of like the absence of value. So this is where we’re not really concerned about the return, the value return. And in this case, we’re doing this ’cause we wanna skip the initial phase. And the reason being is the initial phase is exactly what we see on our preview right now. So there is no sense in trying to animate that at that time.
So one of the first things we wanna do is we’re going to want to animate the rotation. So what we give here is we give a key path to the animate values rotation property. And here this is basically, each one of these parameters here, like we look at the spring key frame. This is the new value we’re gonna set to rotation: first zero degrees, then negative 30 degrees, then positive 30 degrees, and then negative 30 degrees, each with their own little bit of a duration. So we’re totally controlling the way this animation is taking place.
We also have down here the next one to animate the horizontal offset. And this one we’re gonna use cubic keyframes. And again, this first parameter here, zero, negative 20, 20, negative 20 and zero, these are all values that we’re setting to the horizontal offset. And remember again, this is a key path to the animation values horizontal offset. And we know it’s animation values because this is what we sent here. This is what determines what we can use for the key path here, like in this shorthand notation.
And then finally we have the scale, and again, we’re gonna change its values from 1.0 to 1.5 and back to 1.0. And this is, like I said, this gives us full control over each individual little step of the animation. Instead of going from, like we’ve seen before, we gave a whole duration for the whole interpolation to take place with the key frames and the key frame track. We are taking full control over really minute details of that interpolation.
So one of the things we wanna show you here is, let’s see what this animation does. We see that it comes right up. Oh, wait a minute. It looks good because I want, there was a mistake I wanted to show you. The animation does not completely come back to its original state.
The reason for that is, and this is why I left it in here, is because this is a good debugging exercise is because the spring key frame you have to understand is that its job is to move towards a specific value. It doesn’t necessarily have to end at that specific value at the end of the duration. It just has to move towards that way. And that’s why we’re seeing in this situation here, that the bell does not come back to its beginning resting spot.
One of the ways to override that is to use a cubic key frame in its place with the same values. And you’ll notice now that our bell comes back to its original position. That’s because the cubic key frame does have, at the end, it does have to be at its ending state.
There’s a lot of comments in this code. I do encourage you to take some time to go through it, read the comments, play around with this, and do that with all these little demos here. Because the more you tinker around with them, the more these things become ingrained.