Part four: animations, transitions, and gestures

Thomas Ricouard
Jul 17 · 5 min read

This story is the fourth part of a collection of articles about making a real world SwiftUI application. I recommend you read parts one, two, and three first — they’ll give you an overview of the application, its capabilities, features, and architecture.

In this article we’ll take a look at how animations and gestures work in SwiftUI.

MovieSwiftUI combines both multiple gestures and animations in a familiar UX. I’ll use its movies discovery feature and walk you through its inner workings.

Animations

But first let me introduce you to the concept of animation in SwiftUI.

The .animation() modifier can be added to any View, after any previous modifiers.

For example, in the code above we have a button that you can toggle on and off. The .scaleEffect()modifier will be animated because we add a .spring() animation on the view. As you click the button, it triggers its action, which will update the isOn property.

There is another way to do it — and it’s important to remember, because in certain cases you’ll want to animate one state change only, without animating the others. We can provide the exact same effect, but do it a bit differently:

Wrapping a @State property mutation within a withAnimation() block updates the view using the provided animation. This way, I can have other @State properties which also have effect on the view, and mutate them without animating my view.

You can also put your animation in a property and reuse it — useful when your animations have custom parameters.

Transition

There’s another feature you can combine with animations — transitions. You can use these if you want to transition a view on and off the screen. Let’s modify our button slightly, so it will make a view appear as you press it:

Now let’s take a look at the code:

We only displays the Text view “I’m here” when the state is on, and we add our transition as a modifier. Now when we press the button and update the state, it will automatically manage the transition as the view is added or removed from our view hierarchy, and combine it with our animation. Magical isn’t it?

Now that we have seen the basis, let’s dig a bit deeper and walk through a more complex example — the one in the gif above.

Combining Gestures and Animations

We’ll start by adding a drag gesture to our poster to make it draggable. To make the initial contact feel impactful we’ll combine it with a very short long press gesture. The user first has to press on the poster, then he’ll be able to drag it around.

This gesture can be defined before we return our body. So we first initiate our LongPressGesture and our DragGesture, and by using the .sequenced function wean make sure our longPressGesture must be recognised before the dragGesture can start.

You can take a look at the Apple documentation for composing gestures — my implementation is just an evolution of their example. They’ll explain how it works much better than I can.

We also make reference to a dragState property. Let’s see how it’s defined, because this is an important piece. This is the property you’ll use later in your view modifier to actually animate it alongside the gesture.

What happens is that we bind our dragState property using the .updating() callback and set it to the desired value, so that the local state stored in our view will reflect the combination and current status of our gestures.

The .updating() function will forward you both gestures. The .first(true) case is when the longPress gesture is validated, and the .second case lets us get information about the DragGesture as it’s updated — i.e. when the user drags the attached view on the screen. When the gesture is cancelled or when the user is finished interacting with our view, by default the state is reset to inactive.

Our dragState will store the current drag gesture translation and also the predicted end location.

Let’s see how we orchestrate that with our view now:

As you can see in the code above, once the gesture is in place, this is just a matter of updating modifiers in your view according to the gesture state. Now the most interesting part is to play with the computeOffset() and computeAngle() function, if you want to add some drag resistance for example.

This is a small and simplified extract from the MovieSwiftUI discover feature, but it’s fully working and will get your started with gestures. You can see the code of the whole feature here. It includes the stacked posters with the scale effect, and also will show you how to detect the end state of the gesture. If the user cancelled it, or if there is enough velocity on the left or on the right to trigger an action.

This article would be much too long if I included all the flow and code for this feature, which is why I’ve just used a simplified example.

You can do many other things with animations, transitions and gestures in SwiftUI. In my opinion, the simplicity of the declarative API allow us to iterate much faster, and it’s very easy to tweak, change and update our animations and gestures. It would have taken me ages and many more lines of code to implement the same feature in UIKit.

I have a few more articles planed in this collection, but they’ll come a little later. As Apple adds stability the SwiftUI API, it’s mostly about how to provide a beautiful macOS UX in the same target. And for now, SwiftUI doesn’t compile in a shared iOS/macOS target (as of beta 3).

Thanks for reading!

Better Programming

Advice for programmers.

Thomas Ricouard

Written by

📱 🚀 🇫🇷 [Entrepreneur, iOS/Mac & Web dev] | Now @glose 📖 | Past @google 🔍 | Co-founded few companies before, a movies 🎥 app and smart browser one.

Better Programming

Advice for programmers.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade