Do you know the Animatable Protocol in SwiftUI? It controls the default animations in SwiftUI. If your variables conform to this protocol, they will animate smoothly. For example:
struct Avatar: View {
@State var selected: Bool = false
var body: some View {
Image(systemName: "heart.fill")
.resizable()
.foregroundStyle(.red)
.frame(width: 48, height: 48)
.position(x: selected ? 300 : 200, y: 200)
.animation(.bouncy, value: selected)
.onTapGesture {
selected.toggle()
}
}
}
when you run this, you'll see a nice animation of the Heart Image, because the position's x and y values conform to the protocol mentioned above.
Now, let's look at another example that does not conform to this protocol:
struct MyPosition: Equatable {
var x: CGFloat
var y: CGFloat
}
struct MyCircle: View {
var position: MyPosition
var body: some View {
GeometryReader { geo in
Path { path in
path.addArc(center: CGPoint(x: position.x, y: position.y),
radius: min(geo.size.width, geo.size.height) / 2,
startAngle: .zero,
endAngle: .degrees(360), clockwise: false)
}
}
}
}
struct MyAnimatableView: View {
@State private var position = MyPosition(x: 0, y: 0)
var body: some View {
MyCircle(position: position)
.frame(width: 64, height: 64)
.foregroundColor(.blue)
.animation(.linear(duration: 1), value: position)
.onTapGesture {
position = MyPosition(x: 150, y: 0)
}
}
}
If you run this, you will see the MyCircle moves abruptly, and is not smooth.This happends because MyCircle does not conform to the Animatable protocol, so it cannot animate properly. If you want MyCircle to move more smoothly, you can add the following code:
extension MyCircle: Animatable {
var animatableData: AnimatablePair<CGFloat, CGFloat> {
get { AnimatablePair(position.x, position.y) }
set {
position.x = newValue.first
position.y = newValue.second
}
}
}
By adding this extension, SwiftUI will understand how to animate the position by changing the newValue.
The essence of the animation is that it creates multiple frames and transitions between them based on delta changes. Therefore, we need to let SwiftUI know which parameters can animate.