Hello televisions and pans, Leo here. Today we will talk about animating view transitions in SwiftUI.

The first thing that should come to your mind is that we will use the SwiftUI Transition API and you are absolutely right. But of course, we will also add some twists with Tab Views too. The transition API is interesting in SwiftUI. There’s only one thing that you should know to use it properly. The transitions in SwiftUI are only triggered because of a view creation or destruction. Understanding that is very important because we will have to use some tricks to make our view work as we want in this article.

Talking about SwiftUI has been a priority for me in this blog. First because is what I’ll use more and more in my work life and for my personal projects. This is what Apple is investing heavily in and is the future, so let’s embrace it fully! In that way, in last week’s article, we talked about how to explore all the push notification options in SwiftUI code. That is very important that you know each one of those and what is suitable for your use case.

Architecture and SwiftUI have been hot topics in the iOS community. Some people are advocating for MVVM, others are talking about Redux-like approaches and others are just saying that the apps should only have Models that would be accessible for whenever they need in the app. All those discussions are valid and interesting but before that, we also have a lot of micro-architecture decisions like what I’ve discussed in the article about closures and interfaces.

Lately, I’ve been playing table tennis again and it’s awesome. I love that sport and is something that I trained in when was a teenager and now I can practice again.

No more talking, let’s code! But first…  

 

Painting of The Day

I chose the “The Carrousel, Grey Weather” painting for today. It was made by Camille Pissarro in 1899. He was a Danish-French artist, born in 1830 on St Thomas. Pissarro was significant in the art world for his contributions to Impressionism and Post-Impressionism. He studied with masters like Courbet and Corot, then later in life at 54, joined the Neo-Impressionism movement with Seurat and Signac.

I chose this painting because today we will create an animated carrousel transition and the name of the painting has carrousel.  

 

The Problem –  How to animate Carrousel Transitions in SwiftUI?

You were asked to create a carrousel where views transition very smoothly between each other.

First let’s show what we want to achieve here:  

 

via GIPHY

As you can see in the example above, the goal is to create this effect. You can use it in a myriad of designs. For example a selection of a background for your app, a selection of an avatar for your users, and much more. Anything that the user can select between a fixed amount of options, could be applied here.

Disclaimer: I know there are infinite ways to achieve this. This is by no intent a thorough tutorial on all the possible ways to do that effect. Our intention here is to try to do as simple as possible. Of course, you can reach me on Twitter or LinkedIn if you think there’s an even easier way. Let’s go to the code example.  

 

SwiftUI Animated Carrousel Code Example

Create a new project, and copy and paste de code below:  

import SwiftUI

final class ColorViewModel: ObservableObject {
    @Published var selectedImage = ""
    
    let imageNames = ["star", "trash", "paperplane.fill", "books.vertical", "eraser.line.dashed.fill"]
    
    init() {
        selectedImage = imageNames.first ?? "star"
    }
    
    func nextSelectImage() {
        guard let currentIndex = imageNames.firstIndex(of: selectedImage) else {
            return
        }
        
        let nextIndex = currentIndex + 1 < imageNames.count ? currentIndex + 1 : 0
        
        withAnimation {
            selectedImage = imageNames[nextIndex]
        }
    }
}

struct CarrouselView: View {
    
    @StateObject var colorViewModel = ColorViewModel()
    
    var body: some View {
        VStack {
            TabView(selection: $colorViewModel.selectedImage) {
                ForEach(colorViewModel.imageNames, id: \.description) { imageName in // in production code, I don't recommend using description of strings as IDs for a forEach.
                    Image(systemName: imageName)
                        .resizable()
                        .frame(width: 300, height: 300)
                        .ignoresSafeArea()
                }
            }.tabViewStyle(.page(indexDisplayMode: .never))
            
            Button("Next Image") {
                colorViewModel.nextSelectImage()
            }
            .buttonStyle(.borderedProminent)
        }
    }
}

In the first draft of this behavior, I thought that pagination could be done just by animating the changes in a tab view page. And that can be done, but then when we have to move to the first element again it rolls back gracefully to it.

And we don’t want that. We want that the first element be automatically the next one when the sequence is over. 

Now we are going to use pure transitions and that will create the interaction that we want, modify the CarrouselView with the content below:  

struct CarrouselView: View {
    
    @StateObject var colorViewModel = ColorViewModel()
    
    var body: some View {
        VStack {
            ForEach(colorViewModel.imageNames, id: \.description) { imageName in
                if imageName == colorViewModel.selectedImage {
                    Image(systemName: imageName)
                        .resizable()
                        .frame(width: 300, height: 300)
                        .ignoresSafeArea()
                        .transition(.asymmetric(insertion: .move(edge: .leading), removal: .move(edge: .trailing)))
                }
            }
            
            Button("Next Image") {
                colorViewModel.nextSelectImage()
            }
            .buttonStyle(.borderedProminent)
        }
    }
}

Let’s explain this code. Do you remember that at the beginning of the article, I said that to use the transition API we need to somehow create or destroy views? We are doing this using the ForEach. Then to create only the view we want we are using an if statement this way we trick SwiftUI to do what we want. Hacky isn’t it?

Observe that we need to create the view inside the FOR each loop to force the view creation/destruction each time we want a new view. I don’t need to say that you shouldn’t use this for lots of data. This was just an experiment, and I think it work very well for a small number of possible views in the carrousel. If you are searching for a more scalable solution probably you would end up with a infinity scroll view/list view or something like that.

And that’s it!  

 

Summary – Animating View Transitions in SwiftUI

Animating view transitions in SwiftUI, particularly creating an animated carousel effect, can be achieved by utilizing the SwiftUI Transition API along with some hacky tricks. The Transition API in SwiftUI is triggered through view creation or destruction, which can be harnessed to create smooth transitions between views.

Fellow Apple Developers, that’s all.

I hope you liked reading this article as much as I enjoyed writing it. If you want to support this blog you can Buy Me a Coffee or say hello on Twitter. I’m available on LinkedIn or send me an e-mail through the contact page.

You can likewise sponsor this blog so I can get my blog free of ad networks.

Thanks for the reading and… That’s all folks.

Image credit: Featured Painting