How it works
EmptyMatchedTransitionSourceConfiguration is the default configuration type SwiftUI uses for a view marked as the source of a matched transition. When you tag a view with matchedTransitionSource(id:in:), the framework records that view's geometry and appearance as the starting point for a matched geometry effect — most commonly a zoom navigation transition — and an empty configuration is what it applies when you don't supply any custom styling. Reach for it implicitly whenever you want a view to act as the visual anchor a presented destination grows out of, without altering how that source is captured.
Establish a shared namespace with @Namespace
A matched transition needs a namespace so SwiftUI can pair the source view with its destination across the presentation boundary. Declare it once with the
@Namespaceproperty wrapper — herenamespace— and hand the same value to both the source and the destination so they resolve to the same transition group.Tag the origin view with matchedTransitionSource(id:in:)
The
matchedTransitionSource(id:in:)modifier is what enrolls a view as a matched transition source, and its default configuration is EmptyMatchedTransitionSourceConfiguration. Applied to theRoundedRectanglewithid: "hero"andin: namespace, it records that rectangle's frame and content as the anchor the destination will animate from.Match the identifier on the destination transition
The configuration only takes effect when a destination claims the same source. The
navigationDestination(isPresented:)content uses.navigationTransition(.zoom(sourceID: "hero", in: namespace)), whosesourceIDand namespace match the source'sidandin:— so the presented view zooms out of the tagged rectangle rather than appearing with a generic push.Drive the presentation that triggers the transition
Because EmptyMatchedTransitionSourceConfiguration carries no styling of its own, the captured source is used as-is; the transition fires when the destination is presented. Toggling
showDetailfrom theonTapGestureflips theisPresentedbinding, and SwiftUI plays the matched zoom using the empty configuration's recorded geometry.
sourceID: "hero" in the .navigationTransition(.zoom(...)) call to a different string and observe the destination fall back to a plain push, since no source matches the empty configuration's recorded id.Example & preview
Press Run live & edit to compile it in your browser — then edit the Swift on the left and the preview re-renders live.
struct EmptyMatchedTransitionSourceConfigurationDemo: View {
@Namespace private var namespace
@State private var showDetail = false
var body: some View {
NavigationStack {
VStack(spacing: 16) {
RoundedRectangle(cornerRadius: 16)
.fill(.blue.gradient)
.frame(width: 120, height: 120)
.matchedTransitionSource(id: "hero", in: namespace)
.overlay(Text("Tap").foregroundStyle(.white).bold())
.onTapGesture { showDetail = true }
Text("Zoom Transition")
.font(.headline)
}
.padding()
.navigationDestination(isPresented: $showDetail) {
RoundedRectangle(cornerRadius: 16)
.fill(.blue.gradient)
.navigationTransition(.zoom(sourceID: "hero", in: namespace))
.overlay(Text("Detail").foregroundStyle(.white).bold())
.padding()
}
}
}
}