[SOLVED] Sliding view up by MatchedGeometryEffect

Issue

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.

enter image description here

Solution

Location of modifier matters, and for matchedGeometryEffect it is important even more.

Here is fixed variant. Xcode 13.2 / iOS 15.2

demo

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()
            }
    }
}

Answered By – Asperi

Answer Checked By – Pedro (BugsFixing Volunteer)

Leave a Reply

Your email address will not be published.