With iOS14 out, you can use matchedGeometryEffect(). If you are using iOS14, I would recommend this approach.
https://www.hackingwithswift.com/quick-start/swiftui/how-to-synchronize-animations-from-one-view-to-another-with-matchedgeometryeffect
https://developer.apple.com/documentation/swiftui/view/matchedgeometryeffect(id:in:properties:anchor:issource:)
So in your solution, if you replace action.toggle() with withAnimation{self.action.toggle()} in your button code, it will animate.
Button("transform") { withAnimation{self.action.toggle()} } .font(Font.largeTitle).padding()
This solution works on the simulator for me (Xcode 12.1, iPhone 11 iOS 14.1):
import SwiftUI struct ContentView: View { @State var action: Bool = false @Namespace var transition var body: some View { ZStack { Group { if action { Circle() .fill(Color.blue).frame(width: 150, height: 150, alignment: .center) .matchedGeometryEffect(id: "shape", in: transition) } else { Rectangle() .fill(Color.red).frame(width: 150, height: 150, alignment: .center) .matchedGeometryEffect(id: "shape", in: transition) } } .animation(.easeInOut) VStack { Spacer() Button("transform") { withAnimation{self.action.toggle()} }.font(Font.largeTitle).padding() } } } }
The matchedGeometryEffect() doesn't want to animate different shapes (including cornerRadius) or colors that nicely, not sure if this is a bug that will get fixed in future patches or just a feature that needs to be worked around by regular animations. With me playing around with matchedGeometryEffect(), it seems to do great with sizing things up and down, like shown with this code:
import SwiftUI struct ContentView: View { @State private var animate: Bool = false @Namespace private var transition var body: some View { VStack { if animate { RoundedRectangle(cornerRadius: 75.0) .matchedGeometryEffect(id: "shape", in: transition) .frame(width: 250, height: 250, alignment: .center) .foregroundColor(Color.blue) .animation(.easeInOut) .onTapGesture { animate.toggle() } } else { // Circle RoundedRectangle(cornerRadius: 75.0) .matchedGeometryEffect(id: "shape", in: transition) .frame(width: 150, height: 150, alignment: .center) .foregroundColor(Color.red) .animation(.easeInOut) .onTapGesture { animate.toggle() } } } } }