SwiftUI not only gives us get automatic animation… it lets us control how the animation occurs, using the animatableData
property. So cool!
Only I can't get it to work. The following code displays a clock face on the screen (numbers 0…11 ????) and shows a toggle switch. Switching the toggle rotates the numbers by 180 degrees, animated. Or that is my intent. But instead of each number taking an animated path around the circle of numbers, each number moves along a straight line to its new position… so that the circle of numbers collapses to the circle's center point and then re-explodes into its new configuration:
The animatableData
property is not being referenced by SwiftUI. Any idea why?
(Note that it wouldn't work to use a standard rotation animation, because that would turn all the numbers upside down.)
import SwiftUI
struct ContentView: View {
@State var state: Bool = false
var body: some View {
VStack {
CircleView(hoursOffset: CGFloat(state ? 6 : 0 ))
.aspectRatio(contentMode: .fit)
Toggle(isOn: $state, label: { Text("Rotate 180°?") })
.frame(maxWidth: 200).padding()
}
}
}
extension CGPoint {
static func onCircle(hours: CGFloat, size: CGFloat) -> CGPoint {
let radians = (3 - hours) * CGFloat.pi / CGFloat (6)
let hypotenuse = size / 2
return CGPoint(x: size / 2 + 0.8 * hypotenuse * cos(radians),
y: size / 2 - 0.8 * hypotenuse * sin(radians))
}
}
struct CircleView: View {
var hoursOffset: CGFloat
public var animatableData: CGFloat {
get { hoursOffset }
set { self.hoursOffset = newValue }
}
var body: some View {
GeometryReader() { geo in
ForEach(0..<12) { hour in
ZStack {
Text("\(hour)")
.position(CGPoint.onCircle(hours: CGFloat(hour) + self.hoursOffset, size: geo.size.width))
.animation(Animation.easeInOut(duration: 2.0))
}
}
}
}
}