SwiftUI Animations MatchedGeometryEffect
SwiftUI Animations: MatchedGeometryEffect
Animate view identity changes across layouts using .matchedGeometryEffect.
Syntax
Matched IDs: Apply .matchedGeometryEffect(id: "id", in: namespace) to two views; SwiftUI animates layout/size/position between them.
Dot Expands to Large Circle
Two circles share the same matched geometry ID.
Toggling state animates size and position between the small and large versions.
Example
import SwiftUI
struct MatchedDemo: View {
@Namespace private var ns
@State private var showDetail = false
var body: some View {
VStack(spacing: 16) {
if showDetail {
Circle().matchedGeometryEffect(id: "dot", in: ns).frame(width: 120, height: 120)
} else {
Circle().matchedGeometryEffect(id: "dot", in: ns).frame(width: 40, height: 40)
}
Button("Toggle") { withAnimation(.spring()) { showDetail.toggle() } }
}
.padding()
}
}
import SwiftUI
struct ContentView: View {
var body: some View { MatchedDemo() }
}
import SwiftUI
@main
struct MyApp: App {
var body: some Scene {
WindowGroup { ContentView() }
}
}
In the example above, the two circles share the same matched geometry ID.
Toggling state animates size and position between the small and large versions.
Card Expands to Detail (Shared ID)
A small card animates into a larger detail view using the same matched geometry ID.
The transition preserves visual continuity.
Example
import SwiftUI
struct CardsMatchedDemo: View {
@Namespace private var ns
@State private var expanded = false
var body: some View {
VStack(spacing: 16) {
if expanded {
RoundedRectangle(cornerRadius: 16)
.fill(.blue.opacity(0.2))
.matchedGeometryEffect(id: "card", in: ns)
.frame(height: 160)
.overlay(Text("Detail").font(.headline))
} else {
RoundedRectangle(cornerRadius: 12)
.fill(.blue.opacity(0.2))
.matchedGeometryEffect(id: "card", in: ns)
.frame(height: 60)
.overlay(Text("Card").font(.subheadline))
}
Button(expanded ? "Close" : "Open") {
withAnimation(.spring()) { expanded.toggle() }
}
}
.padding()
}
}
import SwiftUI
struct ContentView: View {
var body: some View { CardsMatchedDemo() }
}
import SwiftUI
@main
struct MyApp: App {
var body: some Scene {
WindowGroup { ContentView() }
}
}
In the example above, the small card animates into a larger detail view using the same matched geometry ID.
The transition preserves visual continuity.