Use SpringAnimator for fine-grained control over individual property animations
While Wave’s block-based API is convenient for most use cases, the SpringAnimator<T> class provides a lower-level API for animating individual properties with precise control over timing, retargeting, and lifecycle events.
Create a SpringAnimator with a spring configuration, initial value, and target:
let animator = SpringAnimator<CGFloat>( spring: Spring(dampingRatio: 0.8, response: 0.6), value: 0.0, target: 1.0)animator.valueChanged = { currentValue in // Use the animated value to update your UI print("Current value: \(currentValue)")}animator.start()
The spring model determining the animation’s motion characteristics. Can be modified at any time.
// Start with tight spring for interactive feelanimator.spring = Spring(dampingRatio: 0.8, response: 0.2)// Later, switch to looser spring for final animationanimator.spring = Spring(dampingRatio: 0.68, response: 0.8)
// Stop immediately at current valueanimator.stop(immediately: true)// Smoothly animate to current value (cancels movement to target)animator.stop(immediately: false)
The state property tracks the animation lifecycle:
switch animator.state {case .inactive: print("Animation hasn't started or has been reset")case .running: print("Animation is currently executing")case .ended: print("Animation completed or was stopped")}
Change the target property while the animation is running to smoothly retarget:
let animator = SpringAnimator<CGPoint>( spring: Spring(dampingRatio: 0.8, response: 0.6), value: CGPoint(x: 0, y: 0), target: CGPoint(x: 100, y: 100))animator.valueChanged = { [weak view] position in view?.center = position}animator.start()// Later, while animation is runninganimator.target = CGPoint(x: 200, y: 50) // Smoothly retargets
When retargeting occurs, the completion handler is called with a .retargeted event:
animator.completion = { event in switch event { case .finished(let finalValue): print("Animation completed at: \(finalValue)") case .retargeted(let from, let to): print("Retargeted from \(from) to \(to)") }}
animator.completion = { event in switch event { case .finished(at: let finalValue): print("Animation finished at: \(finalValue)") case .retargeted: break }}
animator.completion = { event in switch event { case .finished: break case .retargeted(from: let oldTarget, to: let newTarget): print("Retargeted from \(oldTarget) to \(newTarget)") }}
Query the estimated duration based on the spring configuration:
let duration = animator.settlingTimeprint("Animation will take approximately \(duration) seconds")
This is useful for debugging but should not be used to determine animation progress. The animation may finish slightly earlier or later than settlingTime.