Using the zoom navigation transition in SwiftUI

Using the zoom navigation transition in SwiftUI

Learn how to use the zoom navigation transition from iOS 18 in a SwiftUI app.

The NavigationTransition protocol allows developers to customize transitions between Views using the .navigationTransition(_:) modifier. Beyond the typical left-to-right movement, a new zoom transition effect has been introduced that can be particularly useful for presenting UI items on a full screen.

In this reference, we will integrate the new Zoom transition to push a View within a NavigationStack.

Implementing a zoom transition in a NavigationStack

To create the zoom transition in our SwiftUI view, we need to establish a connection between the source and destination views. This is achieved by defining a namespace property, which links the two views.

import SwiftUI

struct ContentView: View {

    @Namespace private var namespace
    
    var body: some View {
        ...
    }
    
}

On the destination view, attach the navigationTransition(_:) modifier passing the zoom(sourceID:in:) method providing a unique identifier and the namespace property declared before.

import SwiftUI

struct ContentView: View {
    @Namespace private var namespace
    
    var body: some View {
        NavigationStack {
            NavigationLink {
                DetailView()
                    .navigationTransition(.zoom(sourceID: "zoom", in: namespace))
            } label: {
                Text("Source view here soon ")
            }
        }
    }
}

struct DetailView: View {
    
    @State var gradientStyle = Gradient(colors: [
        .blue, .purple, .red, .orange, .yellow
    ])
    
    var body: some View {
        
        VStack {
            Image(systemName: "swift")
                .font(.largeTitle)
                .foregroundStyle(.white)
                
                .background {
                    Rectangle()
                        .fill(
                            LinearGradient(
                                gradient: gradientStyle,
                                startPoint: .leading,
                                endPoint: .trailing
                            )
                        )
                        .frame(width: 600, height: 200)
                        .ignoresSafeArea()
                }
            
            Spacer()
        }
    }
}

The source view will serve as the label for our NavigationLink, to which we will attach the matchedTransitionSource(id:,in:) modifier, ensuring that the same identifier used previously is passed.

import SwiftUI

struct ContentView: View {
    @Namespace private var namespace
    
    var body: some View {
        NavigationStack {
            NavigationLink {
                DetailView()
                    .navigationTransition(.zoom(sourceID: "zoom", in: namespace))
            } label: {
                SourceView()
                    .matchedTransitionSource(id: "zoom", in: namespace)
            }
        }
    }
}

struct DetailView: View {
    ...
}

struct SourceView: View {
    
    var body: some View {
        RoundedRectangle(cornerRadius: 25)
            .fill(Color.black)
            .frame(width: 250, height: 200)
            
            .overlay {
                Text("Create with Swift")
                    .font(.title)
                    .fontDesign(.rounded)
                    .foregroundStyle(.white)
            }
    }
    
}

The zoom transition allows for a smooth zooming effect during view navigation, enhancing visual flow.

Unlike the matched geometry effect, which customizes transitions by animating shared elements between views, navigation transition using the zoom style provides a predefined effect animation for a continuous navigation experience.

Slow motion video of the navigation transition

Both create smooth animations the difference is that using matched geometry offers more flexibility for element-specific transitions, while the zoom navigation transition style is simpler for zoom-based navigation effects.