When Apple introduced NavigationTransition in iOS 18, it finally gave SwiftUI developers a first-party way to customize navigation animations. The initial API focused on the zoom transition, enabling beautiful App Store-style hero animations between a source and destination view.
While the zoom transition is excellent for image galleries, cards, and grid layouts, it isn't always the right choice. Many apps simply need a subtle fade between screens without establishing a relationship between source and destination views.
That's exactly what Apple addressed at WWDC 2026 by introducing NavigationTransition.crossFade in SwiftUI.
Instead of zooming from a source view, the framework simply fades the outgoing view while fading in the destination view, resulting in a clean and distraction-free navigation experience.
📖 Apple Documentation: https://developer.apple.com/documentation/SwiftUI/NavigationTransition/crossFade
NavigationTransition.crossFade?NavigationTransition.crossFade is a built-in navigation transition that cross-fades between the disappearing view and the appearing view.
Unlike the existing zoom transition, it doesn't require:
@NamespacematchedTransitionSourcesourceIDYou simply attach the transition to the presented or destination view.
.navigationTransition(.crossFade)That's all.
This makes it one of the simplest navigation animation APIs Apple has added to SwiftUI.
If you wanted a fade animation, you typically had to:
crossDissolveNone of these solutions felt truly native.
Now SwiftUI provides it out of the box with a single modifier.
One of the easiest ways to see NavigationTransition.crossFade in action is with a sheet presentation.
Without crossFade, a sheet uses the standard presentation animation. By applying .navigationTransition(.crossFade), the presented view fades smoothly into place, creating a lighter and less distracting transition.
import SwiftUI
struct ContentView: View {
@State private var showSettings = false
var body: some View {
Button("Show Settings") {
showSettings = true
}
.sheet(isPresented: $showSettings) {
SettingsView()
.navigationTransition(.crossFade)
}
}
}
struct SettingsView: View {
@Environment(\.dismiss) private var dismiss
var body: some View {
NavigationStack {
Form {
Toggle("Enable Notifications", isOn: .constant(true))
Toggle("Dark Mode", isOn: .constant(false))
}
.navigationTitle("Settings")
.toolbar {
Button("Done") {
dismiss()
}
}
}
}
}Run the example and compare it with the same code after removing .navigationTransition(.crossFade).
The difference is immediately noticeable. Instead of using the standard sheet presentation animation, the sheet gently fades into view, creating a more subtle transition.
The biggest advantage of crossFade is simplicity.
Compare it with the existing zoom transition.
@Namespace private var animation
DetailView()
.navigationTransition(
.zoom(sourceID: item.id, in: animation)
)
.matchedTransitionSource(
id: item.id,
in: animation
)Now compare it with crossFade.
DetailView()
.navigationTransition(.crossFade)No namespace.
No matching IDs.
No source views.
No extra setup.
This makes crossFade an excellent choice whenever the transition is about moving between screens rather than visually connecting two views.
Although both APIs belong to NavigationTransition, they solve different problems.
| Zoom | Cross-Fade |
|---|---|
| Animates from a source view | Fades between entire screens |
| Requires a matched transition source | No source view required |
| Requires a namespace | No namespace |
| Best for cards, grids, and images | Best for forms, settings, and editors |
| Creates a visual relationship between views | Simply transitions between screens |
A simple way to think about them is:
Choosing the appropriate transition helps users better understand the relationship between screens.
Not every navigation animation should draw attention to itself.
Many business and productivity apps benefit from a more subtle transition.
Settings pages usually don't have a visual element that should zoom into the next screen.
NotificationSettingsView()
.navigationTransition(.crossFade)A fade feels much more natural here.
Imagine navigating from an invoice list to a Create Invoice screen.
Invoices
↓
Create InvoiceThere's no reason to zoom from the selected row.
A cross-fade creates a calmer and more professional experience.
This works especially well for productivity apps where users frequently move between forms.
Welcome
↓
Login
↓
Verification
↓
HomeEach screen represents a new step rather than an expanded version of the previous one.
A fade transition communicates that progression naturally.
Applications such as:
often navigate from a list into a full-screen editing experience.
Since the destination isn't visually connected to the selected row, a fade animation feels cleaner than a zoom.
Consider a payment flow.
Payment
↓
SuccessThe success screen isn't related to a particular UI element on the previous screen.
A cross-fade provides a polished finish without distracting users.
A useful rule of thumb is to ask yourself:
Does the destination represent an enlarged version of something the user tapped?
If the answer is yes, use zoom.
Examples include:
If the answer is no, crossFade is usually the better choice.
Examples include:
Using the right transition makes navigation feel more intentional and polished.
Another advantage of crossFade is that it requires virtually no configuration.
Unlike zoom transitions, SwiftUI doesn't need to locate a matching source view or maintain a namespace relationship.
You simply declare the transition and let SwiftUI handle the animation.
Besides reducing boilerplate code, this also makes the API easy to adopt throughout an existing project.
NavigationTransition.crossFade is available starting with:
If your app supports earlier OS versions, you'll need to conditionally apply the transition or fall back to the default navigation animation.
Apple Developer Documentation – Cross Fade Navigation Transition
https://developer.apple.com/documentation/SwiftUI/NavigationTransition/crossFade
Apple Developer Documentation – NavigationTransition
https://developer.apple.com/documentation/swiftui/navigationtransition
NavigationTransition.crossFade is a small addition to SwiftUI, but it's one many developers have been waiting for.
The original navigation transition APIs focused on creating visual continuity through zoom animations. While powerful, they required namespaces, matched transition sources, and a clear relationship between the source and destination views.
The new crossFade transition takes a different approach. It embraces simplicity by letting you fade naturally between screens with a single modifier - no source view, no matching IDs, and no additional setup.
Whether you're presenting a settings sheet or navigating to another screen, crossFade provides a subtle transition with almost no code.
Sometimes the best animation is the one users barely notice. With NavigationTransition.crossFade, SwiftUI now has a built-in transition that feels subtle, modern, and effortless to adopt.
If you have suggestions or opinion, feel free to connect with me on X and send me a DM. If this article helped you, Buy me a coffee.