These are the lesson notes for this day. Its build-along tutorial and flash cards have not been written yet.
A transition describes how a view appears and disappears: .opacity, .slide, .scale, .move(edge:), or a combination. It applies when a view is inserted into or removed from the hierarchy, inside an animation. matchedGeometryEffect links two views in different places so that one appears to move and resize into the other — the "hero" effect. For animation with more than two stages, PhaseAnimator steps through a list of states, and KeyframeAnimator gives each property its own timeline.
Keep the two ideas apart. An animation changes a value on a view that stays. A transition happens when a view enters or leaves.
ExerciseShow a grid of thumbnails. Tapping one expands it into a full-size card, using matchedGeometryEffect with a shared namespace.
matchedGeometryEffect need in order to connect two views?KeyframeAnimator instead of withAnimation?“Design wants this card to grow smoothly when tapped. Instead it cross-fades between two sizes. Why? And what is the one idea behind the fix?”
struct ExpandingCard: View {
@State private var isExpanded = false
var body: some View {
VStack {
if isExpanded {
RoundedRectangle(cornerRadius: 16)
.fill(.orange)
.frame(height: 300)
} else {
RoundedRectangle(cornerRadius: 16)
.fill(.orange)
.frame(height: 100)
}
}
.onTapGesture {
withAnimation(.spring) { isExpanded.toggle() }
}
}
}
Where this comes from: Senior iOS Interview Preparation (GitHub) · WWDC21: Demystify SwiftUI (2021 — still the reference on identity)
To SwiftUI, the two branches of an if/else are two different views. Toggling removes one and inserts the other, and the default transition for removal and insertion is a fade. Nothing was ever asked to change size.
struct ExpandingCardFixed: View {
@State private var isExpanded = false
var body: some View {
RoundedRectangle(cornerRadius: 16)
.fill(.orange)
// one view, one identity; only a value changes
.frame(height: isExpanded ? 300 : 100)
.onTapGesture {
withAnimation(.spring) { isExpanded.toggle() }
}
}
}The one idea: an animation interpolates a value on a view whose identity stays the same. Change the identity and you get a transition instead. If two genuinely different views must appear to turn into one another, that is what matchedGeometryEffect is for.
What interviewers listen for: The sentence "the identity changed, so I got a transition, not an animation." It shows you know why, and not only the fix.