matchedGeometryEffect
method to synchronize multiple views’ animations. Open AwardsView.swift under the AwardsView group. When you tap an award, it transitions to a view displaying its details. You’ll change it to pop up the award details over the grid.
flightNavigation
:
@State var selectedAward: AwardInformation?
nil
. Because that tap action occurs in a subview, you need to pass this into that subview.
awards
property:
@Binding var selected: AwardInformation?
AwardsView
to the AwardGrid
. Replace the NavigationLink
in the ForEach
loop with:
AwardCardView(award: award)
.foregroundColor(.black)
.aspectRatio(0.67, contentMode: .fit)
.onTapGesture {
selected = award
}
onTapGesture(count:perform:)
modifier to set the binding to the tapped award.
You also must update the preview to add the new binding parameter. Change it to:
AwardGrid(
title: "Test",
awards: AppEnvironment().awardList,
selected: .constant(nil)
)
Now, go back to AwardsView.swift and change the view’s body to:
ZStack {
// 1
if let award = selectedAward {
// 2
AwardDetails(award: award)
.background(Color.white)
.shadow(radius: 5.0)
.clipShape(RoundedRectangle(cornerRadius: 20.0))
// 3
.onTapGesture {
selectedAward = nil
}
// 4
.navigationTitle(award.title)
} else {
ScrollView {
LazyVGrid(columns: awardColumns) {
AwardGrid(
title: "Awarded",
awards: activeAwards,
selected: $selectedAward
)
AwardGrid(
title: "Not Awarded",
awards: inactiveAwards,
selected: $selectedAward
)
}
}
.navigationTitle("Your Awards")
}
}
.background(
Image("background-view")
.resizable()
.frame(maxWidth: .infinity, maxHeight: .infinity)
)
ZStack
that shows one of two views depending on the if
statement results. The code in the else
condition didn’t change other than passing the binding to the selectedAward
state variable. Some changes are worth noting:
-
selectedAward
state variable. If that fails, you show the grid as before in theelse
part of the statement.
The first change is that you attempt to unwrap the -
AwardDetails
view that was previously theNavigationLink
target.
If the unwrapping succeeds, you display the -
selectedAward
state variable back tonil
. This change removes theAwardDetails
view and displays the grid.
When the user taps the view, you set the - You set the title to the current award’s name.
Run the app. Tap Your Awards and then tap any award in the grid. You see the view changes to the large details display for the award. Tap once more and the grid reappears.
onTapGesture
modifier in AwardsView
, under comment three, and change it to:
.onTapGesture {
withAnimation {
selectedAward = nil
}
}
onTapGesture
modifier in AwardGrid
and change it to:
.onTapGesture {
withAnimation {
selected = award
}
}
matchedGeometryEffect(id:in:properties:anchor:isSource:)
comes in. It lets you connect the two view transitions.
id
works much like other IDs you’ve encountered in SwiftUI. It uniquely identifies a connection, so giving two items the same id
links their animations. You pass a Namespace
to the in
property. The namespace groups related items, and the two together define unique links between views.
AwardsView
, add the following code after the selectedAward
state variable.
@Namespace var cardNamespace
Now, you have a unique namespace for the method.
onTapGesture
modifier is attached to AwardDetails
(before comment 4), add the following:
.matchedGeometryEffect(
id: award.hashValue,
in: cardNamespace,
anchor: .topLeading
)
hashValue
property as the identifier along with your created namespace. You can use any identifier as long as it’s unique in the namespace and consistent. You also specify the anchor
parameter to identify a location in the view used to produce the shared values. It’s not always needed, but in this case, it improves the animation.
AwardGrid
s in the LazyVGrid
to add the namespace as a parameter:
AwardGrid(
title: "Awarded",
awards: activeAwards,
selected: $selectedAward,
namespace: cardNamespace
)
AwardGrid(
title: "Not Awarded",
awards: inactiveAwards,
selected: $selectedAward,
namespace: cardNamespace
)
selected
binding, add the following code:
var namespace: Namespace.ID
Namespace.ID
type. Add the following code after the onTapGesture(count:perform:)
call:
.matchedGeometryEffect(
id: award.hashValue,
in: namespace,
anchor: .topLeading
)
hashValue
property on the award, the same as you used in the parent view. SwiftUI knows to link the transitions when you pass in the same namespace.
You also need to update the preview to pass this new parameter:
#Preview {
@Namespace var namespace
return AwardGrid(
title: "Test",
awards: AppEnvironment().awardList,
selected: .constant(nil),
namespace: namespace
)
}
AwardDetails
view. Similarly, when you tap AwardDetails
view, it appears to shrink and move back to the smaller view in the grid.
matchedGeometryEffect()
only arranges for the views’ geometry to be linked. The usual transition mechanisms applied to the views still occur during the transition.