skip to Main Content

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

2

Answers


  1. Chosen as BEST ANSWER

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

    enter image description here


  2. 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()
                }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search