I am using the following approach to add rounded corners to a view on x number of corners:
Round Specific Corners SwiftUI
extension View {
func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View {
clipShape( RoundedCorner(radius: radius, corners: corners) )
}
}
struct RoundedCorner: Shape {
var radius: CGFloat = .infinity
var corners: UIRectCorner = .allCorners
func path(in rect: CGRect) -> Path {
let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
return Path(path.cgPath)
}
}
This works very well. Unfortunately when I animate this view to another Frame without any rounded corners there is no animation for the cornerRadius. All other animations work fine.
To Illustrate this, the following shows the corner radius animation with the standard .cornerRadius modifier and with the custom .cornerRadius modifier using the extension above:
struct ContentView: View {
@State var radius: CGFloat = 50
var body: some View {
VStack {
Button {
withAnimation(.easeInOut(duration: 2)) {
if radius == 50 {
radius = 0
} else {
radius = 50
}
}
} label: {
Text("Change Corner Radius")
}
Color.red
.frame(width: 100, height: 100)
.cornerRadius(radius, corners: [.topLeft, .bottomRight])
Color.blue
.frame(width: 100, height: 100)
.cornerRadius(radius)
}
}
}
The issue is in the RoundedCorner
struct. It was not written with animations in mind. While a struct conforming to the Shape
protocol is animatable, it won't animate without a var animatableData
in it that provides the ability for the system to understand how to animate the Shape
. I don't know why it isn't required to be implemented, because it is usually pretty trivial to do, as in this case.
Change your RoundedCorner
struct to the following, and it will animate like you want it to:
struct RoundedCorner: Shape {
var radius: CGFloat
var corners: UIRectCorner
var animatableData: CGFloat {
get { return radius }
set { radius = newValue }
}
func path(in rect: CGRect) -> Path {
let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
return Path(path.cgPath)
}
}