Skip to main content
SpringAnimator provides direct control over spring animations, allowing you to manually manage animation state, update values, and respond to animation events.

Overview

SpringAnimator is a generic class that can animate any type conforming to SpringInterpolatable. While most developers will use the high-level Wave.animate() API, SpringAnimator is useful when you need:
  • Fine-grained control over animation lifecycle
  • Custom animation types
  • Manual animation updates
  • Dynamic retargeting during animation

Type parameters

SpringAnimator is a generic class:
public class SpringAnimator<T: SpringInterpolatable>
where T is the type being animated (e.g., CGFloat, CGPoint, CGRect, etc.).

Initializer

init(spring:value:target:)

Creates a new animation with a given Spring, and optionally, an initial and target value.
public init(
    spring: Spring,
    value: T.ValueType? = nil,
    target: T.ValueType? = nil
)
spring
Spring
required
The spring model that determines the animation’s motion.
value
T.ValueType?
default:"nil"
The initial, starting value of the animation. While optional in the initializer, it must be set to a non-nil value before the animation can start.
target
T.ValueType?
default:"nil"
The target value of the animation. While optional in the initializer, it must be set to a non-nil value before the animation can start.
While value and target are optional in the initializer, they must be set to non-nil values before the animation can start.

Properties

id

A unique identifier for the animation.
public let id: UUID

state

The execution state of the animation (inactive, running, or ended).
public private(set) var state: AnimatorState
Possible values:
  • .inactive: The animation is not currently running, but is ready
  • .running: The animation is currently active and executing
  • .ended: The animation has just stopped, and will be reset to the inactive state

spring

The spring model that determines the animation’s motion.
public var spring: Spring

value

The current value of the animation. This value will change as the animation executes.
public var value: T.ValueType?
value needs to be set to a non-nil value before the animation can start.

target

The current target value of the animation.
public var target: T.ValueType?
You may modify this value while the animation is in-flight to “retarget” to a new target value.

velocity

The current velocity of the animation.
public var velocity: T.VelocityType
If animating a view’s center or frame with a gesture, you may want to set velocity to the gesture’s final velocity on touch-up.

valueChanged

The callback block to call when the animation’s value changes as it executes. Use the currentValue to drive your application’s animations.
public var valueChanged: ((_ currentValue: T.ValueType) -> Void)?

completion

The completion block to call when the animation either finishes, or “re-targets” to a new target value.
public var completion: ((_ event: Event) -> Void)?

mode

The animation’s mode. If set to .nonAnimated, the animation will snap to the target value when run.
public var mode: AnimationMode
Default value: .animated

integralizeValues

Whether the values returned in valueChanged should be integralized to the screen’s pixel boundaries. This helps prevent drawing frames between pixels, causing aliasing issues.
public var integralizeValues: Bool
Default value: false
Enabling integralizeValues effectively quantizes value, so don’t use this for values that are supposed to be continuous.

settlingTime

How long the animation will take to complete, based off its spring property.
public var settlingTime: TimeInterval
This is useful for debugging purposes only. Do not use settlingTime to determine the animation’s progress.

Methods

start(afterDelay:)

Starts the animation (if not already running) with an optional delay.
public func start(afterDelay delay: TimeInterval = 0)
delay
TimeInterval
default:"0"
The amount of time (measured in seconds) to wait before starting the animation.

stop(immediately:)

Stops the animation at the current value.
public func stop(immediately: Bool = true)
immediately
Bool
default:"true"
If true, the animation stops immediately at the current value. If false, the animation will animate to the current value (sets target to the current value).

Event enumeration

SpringAnimator.Event represents animation lifecycle events:

finished(at:)

Indicates the animation has fully completed.
case finished(at: T.ValueType)

retargeted(from:to:)

Indicates that the animation’s target value was changed in-flight (i.e. while the animation was running).
case retargeted(from: T.ValueType, to: T.ValueType)
from
T.ValueType
The previous target value of the animation.
to
T.ValueType
The new target value of the animation.

Example usage

Basic animation

let spring = Spring(dampingRatio: 0.7, response: 0.4)
let animator = SpringAnimator<CGPoint>(spring: spring)

animator.value = view.center
animator.target = targetPoint

animator.valueChanged = { currentValue in
    view.center = currentValue
}

animator.completion = { event in
    switch event {
    case .finished(let finalValue):
        print("Animation finished at \(finalValue)")
    case .retargeted(let from, let to):
        print("Animation retargeted from \(from) to \(to)")
    }
}

animator.start()

Animating with gesture velocity

let animator = SpringAnimator<CGPoint>(spring: .defaultInteractive)

animator.value = draggedView.center
animator.target = finalPosition
animator.velocity = gestureVelocity // Inject gesture velocity

animator.valueChanged = { currentValue in
    draggedView.center = currentValue
}

animator.start()

Retargeting an animation

// Start animation
animator.value = view.center
animator.target = CGPoint(x: 100, y: 100)
animator.start()

// Later, while animation is running, retarget to a new position
animator.target = CGPoint(x: 200, y: 200)
// Animation smoothly redirects to the new target

Delayed animation

let animator = SpringAnimator<CGFloat>(spring: .defaultAnimated)

animator.value = 0.0
animator.target = 1.0

animator.valueChanged = { currentValue in
    view.alpha = currentValue
}

// Start after 0.5 seconds
animator.start(afterDelay: 0.5)

Stopping an animation

// Stop immediately at current value
animator.stop(immediately: true)

// Or animate to current value
animator.stop(immediately: false)
Use integralizeValues = true when animating positions to prevent subpixel rendering artifacts, but avoid it for smooth animations like opacity or scale.

Build docs developers (and LLMs) love