I'd like to use .matchedGeometryEffect
to achieve a slide up animation for an image when it's tapped. I only want to use this to animate the position change, as the frame is the same for both images. I'm replacing the image with a black rectangle in this example, as it's reproducible with that as well.
Simplified code:
struct ContentView: View {
@Namespace var namespace
@State var isShowingDetail = false
var body: some View {
if isShowingDetail {
VStack {
Rectangle()
.foregroundColor(.black)
.matchedGeometryEffect(id: "image", in: namespace, properties: .position)
.aspectRatio(contentMode: .fit)
.edgesIgnoringSafeArea(.top)
Spacer()
}
.onTapGesture {
changeView()
}
} else {
Rectangle()
.foregroundColor(.black)
.matchedGeometryEffect(id: "image", in: namespace, properties: .position)
.aspectRatio(contentMode: .fit)
.cornerRadius(8)
.onTapGesture {
changeView()
}
}
}
private func changeView() {
withAnimation(.easeOut(duration: 0.3)){
isShowingDetail.toggle()
}
}
}
The the view slides up, but it doesn't animate properly, as it has a fade effect. I assume it's related to the default fade transition applied to every view, I tried to set a custom one couldn't find a proper one for this case.
CodePudding user response:
Location of modifier matters, and for matchedGeometryEffect
it is important even more.
Here is fixed variant. Xcode 13.2 / iOS 15.2
var body: some View {
if isShowingDetail {
VStack {
Rectangle()
.foregroundColor(.black)
.aspectRatio(contentMode: .fit)
.edgesIgnoringSafeArea(.top)
// !! applied here !!
.matchedGeometryEffect(id: "image", in: namespace, properties: .position)
Spacer()
}
.onTapGesture {
changeView()
}
} else {
Rectangle()
.foregroundColor(.black)
.aspectRatio(contentMode: .fit)
.cornerRadius(8)
// !! applied here !!
.matchedGeometryEffect(id: "image", in: namespace, properties: .position)
.onTapGesture {
changeView()
}
}
}
CodePudding user response:
I figured out the blink/fade effect removal part. As I assuemed, it had to do with the default fade transition that applies if there is no explicit transition. I found a proper one for this case:
if isShowingDetail {
VStack {
Rectangle()
.foregroundColor(.black)
.aspectRatio(contentMode: .fit)
.matchedGeometryEffect(id: "image", in: namespace, properties: .position)
.edgesIgnoringSafeArea(.top)
Spacer()
}
.transition(.offset()) // applied here
.onTapGesture {
changeView()
}
} else {
Rectangle()
.foregroundColor(.black)
.aspectRatio(contentMode: .fit)
.matchedGeometryEffect(id: "image", in: namespace, properties: .position)
.transition(.offset()) // applied here
.onTapGesture {
changeView()
}
}