Matched Transitions in SwiftUI
Matched Geometry and the Zoom navigation transition
Unarguably, one of the biggest advantages of SwiftUI is the ability to create complex UIs and UI interactions with little to no effort. One tool a developer's Swiss Army knife is the matched view effects. In this post, we are going to dive into how matchedGeometryEffect
and navigationTransition
can be used to easily elevate your UI to the next level.
matchedGeometryEffect
This modifier is used to synchronise the geometry of two views on the UI, creating the illusion that the two views are the same. Or in other words, when one view is removed and an other is added to the hierarchy, the inserted view will appear identical to the removed one animating to its new position instead of fading in directly at the target location.
The matchedGeometryEffect
modifier is useful when major changes need to happen to the UI based on some state change, that alters the hierarchy’s structure to such complexity that would not be possible using only one view. Let’s see an example:
struct SomeView: View {
@Namespace private var namespace
@State private var open = false
var body: some View {
VStack {
HStack {
Text("Some content")
if !open {
RoundedRectangle(cornerRadius: 10)
.fill(.red)
.frame(width: 100, height: 100)
.onTapGesture {
withAnimation {
self.open.toggle()
}
}
}
}
if open {
RoundedRectangle(cornerRadius: 10)
.fill(.blue)
.frame(width: 200, height: 200)
.onTapGesture {
withAnimation {
self.open.toggle()
}
}
}
}
}
}
If you run this code and click the red rectangle, you will see that it fades out and the bigger blue one fades in.
It’s ok but we can do better, let’s see how
struct SomeView: View {
@Namespace private var namespace
@State private var open = false
var body: some View {
VStack {
HStack {
Text("Some content")
if !open {
RoundedRectangle(cornerRadius: 10)
.fill(.red)
.matchedGeometryEffect(id: "shape", in: namespace) // new
.frame(width: 100, height: 100)
.onTapGesture {
withAnimation {
self.open.toggle()
}
}
}
}
if open {
RoundedRectangle(cornerRadius: 10)
.fill(.blue)
.matchedGeometryEffect(id: "shape", in: namespace) // new
.frame(width: 200, height: 200)
.onTapGesture {
withAnimation {
self.open.toggle()
}
}
}
}
}
}
As you can see, all we need to do is to create a namespace, and attach the right modifier to the view. It’s important to use the same id and namespace for both of the views, otherwise the system will not know how to handle your hierarchy.
❗️Also note that the id should not appear in more views, otherwise the result will be undefined
navigationTransition
matchedGeometryEffect
is a truly powerful tool, but it can’t be used for navigation between screens in a NavigationStack
. For that we have navigationTransition
and its corresponding matchedTransitionSource
modifier.
At the time of writing this article, the only option for the navigation transition is zoom
. Let’s see how it works
struct ContentView: View {
@Namespace private var namespace
var body: some View {
NavigationStack {
List {
ForEach(0..<10) { i in
NavigationLink {
Destination()
} label: {
HStack {
Text("#\(i)")
}
}
}
}
}
}
}
As you can see, the regular push navigation is used, not let’s set up the zoom animation for our items:
struct ContentView: View {
@Namespace private var namespace
var body: some View {
NavigationStack {
List {
ForEach(0..<10) { i in
NavigationLink {
Destination()
.navigationTransition(.zoom(sourceID: "zoom\(i)", in: namespace))
} label: {
HStack {
Text("#\(i)")
}
.matchedTransitionSource(id: "zoom\(i)",
in: namespace)
}
}
}
}
}
}
Related articles
Here are some more articles that may interest you. Check them out!
App Enums and App Entities
published on September 20, 2025
SwiftAppIntentsAppEnum and AppEntity let you extend AppIntents in iOS with static and dynamic data, making your app’s features more accessible through Siri, Shortcuts, and Spotlight. This article explains how to use both for richer, more discoverable user experiences.
Read moreAppIntents
published on September 18, 2025
SwiftAppIntentsAppIntents in iOS make it easier to connect your app’s features with system-wide experiences like Siri, Shortcuts, and Spotlight. Instead of writing complex integrations, you can declare clear, type-safe intents that expose your app’s functionality to users in a natural way. This not only improves discoverability but also gives users faster, more personalized interactions with your app.
Read moreSwiftUI Transitions
published on September 7, 2025
SwiftSwiftUIAnimationsLearn how to create smooth, engaging UI animations in SwiftUI using built-in and custom transitions. From simple fades to complex combinations, this guide covers the essentials with best practices and examples.
Read more