Reputation: 1817
My home view contains a CustomView
that opens a detailed view via a NavigationLink
when tapped. The detailed view also contains the CustomView
, just in a different location.
Can I use the match geometry effect to transition/animate the location of the CustomView
when the navigation link is clicked?
struct HomeView: View {
@Namespace var namespace
var body: some View {
NavigationStack {
VStack {
Text("Top")
NavigationLink {
DetailView(namespace: namespace)
} label: {
CustomView()
.matchedGeometryEffect(id: "testId", in: namespace)
}
Text("Bottom")
}
}
}
}
struct DetailView: View {
var namespace: Namespace.ID
var body: some View {
VStack {
CustomView()
.matchedGeometryEffect(id: "testId", in: namespace)
Text("Details")
Spacer()
}
}
}
Upvotes: 13
Views: 2365
Reputation: 933
Using the animation modifier can impact the performance, may not be the best solution, but still you can give it a try
struct HomeView: View {
@Namespace var namespace
var body: some View {
NavigationStack {
VStack {
Text("Top")
NavigationLink {
DetailView(namespace: namespace)
} label: {
CustomView()
.matchedGeometryEffect(id: "testId", in: namespace)
.animation(.easeInOut(duration: 0.5))
}
Text("Bottom")
}
}
}
}
struct DetailView: View {
var namespace: Namespace.ID
var body: some View {
VStack {
CustomView()
.matchedGeometryEffect(id: "testId", in: namespace)
.animation(.easeInOut(duration: 0.5))
Text("Details")
Spacer()
}
}
}
Upvotes: 0
Reputation: 1445
I'm going to say, "no, you can't use .matchedGeometryEffect
to animate the position of a view across a NavigationStack transition."
Some details: first of all, the .matchedGeometryEffect will not work without an animation block. So you could rewrite your code to something like this:
struct HomeView: View {
@Namespace var namespace
@State private var showDetail = false
var body: some View {
NavigationStack {
VStack {
Text("Top")
Button {
withAnimation() {
showDetail.toggle()
}
} label: {
CustomView
.matchedGeometryEffect(id: "testId", in: namespace)
}
Text("Bottom")
}
.navigationDestination(isPresented: $showDetail) {
DetailView(namespace: namespace)
}
}
}
}
private struct DetailView: View {
var namespace: Namespace.ID
var body: some View {
VStack {
CustomView()
.matchedGeometryEffect(id: "testId", in: namespace)
Text("Details")
Spacer()
}
}
}
This gives you an opportunity to use withAnimation
in a way that matchedGeometryEffect requires. However, you still don't get the animation you want, leading me to the conclusion above.
I would speculate that Navigation transitions are different from normal SwiftUI transitions. A clue supporting this is that any .transition you could apply to the things in DetailView do not trigger during transition. Another clue is that using withAnimation(.easeIn(duration: 5))
does not give you a 5 second transition.
Your best bet might be to not use NavigationStack. Just use a conditional (with animated transition) to switch between the two layouts. I would also recommend you spend a few days studying the excellent resources on the Swift UI Lab site, with this being a particularly relevant page for you.
I hope that helps!
Upvotes: 2