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, while at first glance that is what appears to happen, if you look closer, you see the transition seems to be more of a crossfade. As it fades out, the source shape does interpolate its corner radius towards the target configuration, but the target shape, as it fades in does not interpolate from the source radius.
This screenshot of the top right section of the animation at approx 50% elapsed illustrates the undesired behavior:
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
}
}
}
}
}





