Interpolating corner radius with matched geometry effect

In the following snippet, it's obvious that matched geometry effect is overlaying both views and crossfading between them. Is there a way to evenly animate the corner radius change?

struct ContentView: View {
 @State var toggled = false
 @Namespace var namespace

 var body: some View {
  VStack(spacing: 16) {
   Toggle("Toggle", isOn: $toggled.animation(.linear(duration: 5)))

   if toggled {
    RoundedRectangle(cornerRadius: 24, style: .continuous)
     .fill(.red)
     .matchedGeometryEffect(id: "box", in: namespace)
     .frame(width: 200, height: 100)

    Spacer()
   } else {
    Spacer()

    RoundedRectangle(cornerRadius: 12, style: .continuous)
     .fill(.blue)
     .matchedGeometryEffect(id: "box", in: namespace)
     .frame(width: 100, height: 200)
   }
  }
  .padding(24)
 }
}

Yes, there is: don't use two RoundedRectangles. 🙂

Instead, use one and mutate the values that you want to differ. Here's how I implemented it in your code:

struct ContentView: View {
    @State var toggled = false
    @Namespace var namespace

    var body: some View {
        VStack(spacing: 16) {
            Toggle("Toggle", isOn: $toggled.animation(.linear(duration: 30)))

            if toggled {
                Spacer()
            }

            RoundedRectangle(cornerRadius: toggled ? 24 : 12, style: .continuous)
                .fill(toggled ? .red : .blue)
                .matchedGeometryEffect(id: "box", in: namespace)
                .frame(width: toggled ? 200 : 100, height: toggled ? 100 : 200)

            if !toggled {
                Spacer()
            }

        }
        .padding(24)

    }
}

How it works:

  • toggled ? value1 : value2 returns value1 if toggled is true or value2 if toggled is false. This works for anything as long as both values are of the same type.
  • @State tells SwiftUI to update the view any time that toggled changes.

So any time you flip the switch, it toggles toggled which forces a recalculation of the view with new values for cornerRadius, .fill, width, and height, and after the recalculation, animates the changes according to your .animation parameters.

I think this gives precisely the kind of animation that you were looking for.

So any time you want to do something like this, instead of having two views that you switch between, use one view and mutate its values.

Interpolating corner radius with matched geometry effect
 
 
Q