Search code examples
iosanimationswiftuiviewtransition

SwiftUI: How to fix prevent transition to happen faster in the y-direction than x-direction?


I am trying to create a transition between two views. As I am currently just testing the transition, both views are very simple.

The first view called "Playground5" contains a small rectangle, which I would like to transition into the second view "Playground 6" which contains a full screen rectangle of the same colour.

I would like the transition to "expand" outwards and "inwards".

The transition between the two views works, but it looks a bit weird.

When the rectangle expands, it reaches the top and bottom before the sides, instead of the same time.see img.

Any ideas on how to solve this?

import SwiftUI

struct Playground5: View {
    @State private var expandRectangle = false
    @Namespace private var namespace

    var body: some View {
        VStack {
            if expandRectangle {
                Playground6(expandRectangle: $expandRectangle, namespace: namespace)
//                    .matchedGeometryEffect(id: "rectangle", in: namespace)
            } else {
                Rectangle()
                    .foregroundColor(Color.red)
                    .frame(width: 200, height: 400)
//                    .frame(width: expandRectangle ? 400 : 200, height: expandRectangle ? 600 : 400)
                    .onTapGesture {
                        withAnimation {
                            expandRectangle.toggle()
                        }
                    }
                    .matchedGeometryEffect(id: "rectangle", in: namespace, properties: .position, anchor: .center)
            }
        }
        .ignoresSafeArea(.all)
    }
}

struct Playground6: View {
    @Binding var expandRectangle: Bool
    var namespace: Namespace.ID
    @Namespace private var namespace2
    var body: some View {
//        ZStack {
            Rectangle()
            .onTapGesture {
                withAnimation {
                    expandRectangle.toggle()
                }
            }
                .foregroundColor(Color.red)
                .frame(maxWidth: .infinity, maxHeight: .infinity)
               
                .ignoresSafeArea(.all)
                
                .matchedGeometryEffect(id: "rectangle",in: namespace)
    }
}

Solution

  • ignoresSafeArea should be set on the container of the entire thing, rather than on each individual component, otherwise (I think) the namespace isn't taking the safe area into account. If I remove the ignoresSafeArea modifier from Playground5 and Playground6 and make the preview like this:

    static var previews: some View {
        Playground5()
            .ignoresSafeArea(.all)
    }
    

    The view scales normally.

    The problem was much easier to see when slowing down the animation, you can see it hits the edges of the safe area and then jumps to fill the rest:

    withAnimation(.easeInOut(duration: 5)) {
        expandRectangle.toggle()
    }