'What does animatableData in SwiftUI do?
I was playing around with the animatableData
property for a custom Shape I made but I couldn't really visualise what it does and where the system uses it.
I didn't understand how the system knows which animatableData
properties it should interpolate when there's a state change. I also didn't understand what the get
part of the animatableData
property is used for by the system. The only thing I sort of understand is that SwiftUI will update the animatableData
property to all the intermediary values between the original and final value for when an @State
variable is changed.
If someone can give a very detailed order of events for the use of animatableData
by the system I'll be extremely grateful. Make it as detailed as you can because I'm one of those people who feels scratchy even if I'm not understanding 1% of something (however if I do have any question I'll just ask you in the comments).
Thanks in advance!
P.S. I tried returning a constant in the getter for animatableData
and my animation still worked perfectly which has confused me even more. Please let me know what the getter is used for if you can.
Solution 1:[1]
The simplest answer to your question is to override the default animatableData
[inherited by the Animatable
protocol] with values used to draw your View. Here's an example of how to do that:
var animatableData: Double {
get { return percent }
set { percent = newValue }
}
Here's an example for you. It:
- Draws a Ring on the parent View.
As the value of percent [which you hook up when you define
animatableData
] changes, the animation updates the view by drawing a line along the circumference of the defined circle using the percent value at the time of the update.
import SwiftUI
/// This repeats an animation until 5 seconds elapse
struct SimpleAnswer: View {
/// the start/stop sentinel
static var shouldAnimate = true
/// the percentage of the circumference (arc) to draw
@State var percent = 0.0
/// animation duration/delay values
var animationDuration: Double { return 1.0 }
var animationDelay: Double { return 0.2 }
var exitAnimationDuration: Double { return 0.3 }
var finalAnimationDuration: Double { return 1.0 }
var minAnimationInterval: Double { return 0.1 }
var body: some View {
ZStack {
AnimatingOverlay(percent: percent)
.stroke(Color.yellow, lineWidth: 8.0)
.rotationEffect(.degrees(-90))
.aspectRatio(1, contentMode: .fit)
.padding(20)
.onAppear() {
self.performAnimations()
}
.frame(width: 150, height: 150,
alignment: .center)
Spacer()
}
.background(Color.blue)
.edgesIgnoringSafeArea(.all)
}
func performAnimations() {
run()
if SimpleAnswer.shouldAnimate {
restartAnimation()
}
/// Stop the Animation after 5 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0, execute: { SimpleAnswer.shouldAnimate = false })
}
func run() {
withAnimation(.easeIn(duration: animationDuration)) {
percent = 1
}
let deadline: DispatchTime = .now() + animationDuration + animationDelay
DispatchQueue.main.asyncAfter(deadline: deadline) {
withAnimation(.easeOut(duration: self.exitAnimationDuration)) {
}
withAnimation(.easeOut(duration: self.minAnimationInterval)) {
}
}
}
func restartAnimation() {
let deadline: DispatchTime = .now() + 2 * animationDuration + finalAnimationDuration
DispatchQueue.main.asyncAfter(deadline: deadline) {
self.percent = 0
self.performAnimations()
}
}
}
/// Draws a Ring on the parent View
/// By default, `Shape` returns the instance of `EmptyAnimatableData` struct as its animatableData.
/// All you have to do is replace this default `EmptyAnimatableData` with a different value.
/// As the value of percent changes, the animation updates the view
struct AnimatingOverlay: Shape {
var percent: Double
func path(in rect: CGRect) -> Path {
let end = percent * 360
var p = Path()
p.addArc(center: CGPoint(x: rect.size.width/2, y: rect.size.width/2),
radius: rect.size.width/2,
startAngle: Angle(degrees: 0),
endAngle: Angle(degrees: end),
clockwise: false)
return p
}
/// This example defines `percent` as the value to animate by
/// overriding the value of `animatableData`
/// inherited as Animatable.animatableData
var animatableData: Double {
get { return percent }
set { percent = newValue }
}
}
#if DEBUG
struct SimpleAnswer_Previews : PreviewProvider {
static var previews: some View {
SimpleAnswer()
}
}
#endif
I found these links to help me answer your question. You should find them useful as well.
Wenderlich - How to Create a Splash Screen With SwiftUI
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |