ios – SwiftUI Matched Geometry Effect: Transition Between Rounded Rectangles with Different Corner Radii


In SwiftUI, I’m working with two screens, Screen A and Screen B. I’m attempting to create a transition effect using SwiftUI’s matched geometry feature. The transition involves two rounded rectangles: one on Screen A, which appears as a circle due to its corner radius and size, and another on Screen B with a zero corner radius, serving as the background.

Expected Behavior:

I anticipated the animation would display as a seamless morphing of a single object from one configuration to another, specifically altering the corner radius, height, and width.

Actual Behavior:

However, the transition seems to be more of a crossfade. The source shape animates its corner radius towards the target configuration, but the target shape, fading in with a static zero corner radius, doesn’t animate from the source radius.

Minimal Example Code:

Main Screen:

enum Screen {
    case main, target
}

@Observable
class Nav {
    var screen:Screen = .main
}

struct ContentView: View {
    
    @Environment(Nav.self) var navController
    
    // matched geometry
    @Namespace var namespace
    
    var body: some View {
        
        switch navController.screen {
        case .main:
            ZStack{
                RoundedRectangle(cornerRadius: 15)
                    .fill(Color.green)
                    .matchedGeometryEffect(id: "circleToBackground", in: namespace)
                    .frame(width: 30, height: 30)
                    .onTapGesture {
                        withAnimation(.easeInOut(duration: 4.0)){
                            navController.screen = .target
                        }
                    }
            }
            
        case .target:
            TargetScreen(namespace: namespace)
        }
        
    }
}

Target Screen:

struct TargetScreen: View {
    
    let namespace: Namespace.ID
    @Environment(Nav.self) var navController
    
    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 0)
                .fill(LinearGradient(colors: [Color.green, Color.white], startPoint: UnitPoint(x:0.5,y:0.0), endPoint: UnitPoint(x:0.5,y:1.0)))
                .matchedGeometryEffect(id: "circleToBackground", in: namespace)
                .frame(maxWidth: .infinity, maxHeight: .infinity)
            
            Text("BACK").onTapGesture {
                withAnimation {
                    navController.screen = .main
                }
            }
        }
    }
    
}

Latest articles

spot_imgspot_img

Related articles

Leave a reply

Please enter your comment!
Please enter your name here

spot_imgspot_img