Issue #995
With iOS 18, SwiftUI introduces matchedTransitionSource and navigationtransition as a powerful new way to create zoom animations between views. This allows you to smoothly transition from a small view to a larger, more detailed view, creating a delightful user experience. This works for both navigating to a new screen or presenting a view as a sheet.
This new feature works very much like the matchedGeometryEffect
, but it is simpler to use and works across different types of presentations like sheets and navigation pushes, where matchedGeometryEffect
can be tricky.
Firstly, we need to create a namespace that both the starting view and the destination view can share. Think of it as a private channel that connects the two views. You create it using the @Namespace
property wrapper, like this:
@Namespace private var animation
- To set the animation’s starting point, add the
.matchedTransitionSource()
modifier to the original view the user interacts with. - To set the navigation transition style on the destination, add the
.navigationTransition()
modifier to the destination view that is being presented.
Below is an example where we have 2 buttons to present and push a detail view
struct MatchedTransitionSourceView: View {
@Namespace private var animation
@State private var showsSheet = false
@State private var navigationPath = NavigationPath()
var body: some View {
NavigationStack(path: $navigationPath) {
VStack(spacing: 50) {
presentButton
navigateButton
}
.navigationTitle("Zoom Animations")
.navigationDestination(for: String.self) { id in
Text("Detail View")
.navigationTransition(.zoom(sourceID: "push", in: namespace))
}
}
.sheet(isPresented: $showsSheet) {
Text("Detail View")
.navigationTransition(.zoom(sourceID: "present", in: namespace))
}
}
private var presentButton: some View {
Button {
showsSheet = true
} label: {
Image(systemName: "moon.stars.fill")
.font(.system(size: 100))
.foregroundStyle(.purple.gradient)
}
.matchedTransitionSource(id: "present", in: animation)
}
private var navigateButton: some View {
Button {
navigationPath.append("push")
} label: {
Image(systemName: "sun.max.fill")
.font(.system(size: 100))
.foregroundStyle(.orange.gradient)
}
.matchedTransitionSource(id: "push", in: animation)
}
}
Start the conversation