One Logo Pool Ball
wwwwwwwwwwwwwwwwwww

iOS Animators

PhaseAnimator and KeyframeAnimator under One.iOS, run by SwiftUI

One.iOS.PhaseAnimator and One.iOS.KeyframeAnimator are SwiftUI’s PhaseAnimator and KeyframeAnimator. SwiftUI runs the timing, interpolation, springs and interruption on the main thread; JavaScript does not drive any frames.

PhaseAnimator

PhaseAnimator(_:trigger:content:animation:) without a trigger cycles through phases while it is on screen. With trigger, each change (compared with Object.is) runs every phase once and returns to the first. A change during a run moves on to the next phase at once.

PropsTypeDescription
phases (required)Iterable<Phase>The phases, in order. Must not be empty.
Iterable<Phase>The phases, in order. Must not be empty.
triggerunknownRuns the phases once each time it changes. Omit it to cycle.
unknownRuns the phases once each time it changes. Omit it to cycle.
animation(phase: Phase) => SDKAnimation | null = 'default'The animation into a phase, or null to change without one.
(phase: Phase) => SDKAnimation | null = 'default'The animation into a phase, or null to change without one.
children (required)(phase: Phase) => ReactNodeThe content closure.
(phase: Phase) => ReactNodeThe content closure.
<One.iOS.PhaseAnimator
phases={[0, 1, 2]}
trigger={likes}
animation={(phase) => (phase === 1 ? 'bouncy' : 'easeInOut')}
>
{(phase) => (
<One.iOS.Image
systemName="heart.fill"
swiftStyle={{ scaleEffectWithSAndAnchor: { s: phase === 1 ? 1.4 : 1, anchor: 'center' } }}
/>
)}
</One.iOS.PhaseAnimator>

The closure runs once per phase during render, and SwiftUI animates from one phase’s swiftStyle to the next. Colors that change by phase must be plain colors; a platform color throws. animation returns one of the named animations withAnimation takes.

KeyframeAnimator

KeyframeAnimator(initialValue:repeating:content:keyframes:) and KeyframeAnimator(initialValue:trigger:content:keyframes:). The value is a number or a record of up to eight numbers. With repeating (the default without a trigger) the timeline loops; with trigger it plays once per change, starting again from the current value when the change lands mid-run.

PropsTypeDescription
initialValue (required)ValueA number, or a record of up to eight numbers.
ValueA number, or a record of up to eight numbers.
repeatingboolean = trueLoops the timeline. Not allowed with trigger.
boolean = trueLoops the timeline. Not allowed with trigger.
triggerunknownPlays the timeline once each time it changes.
unknownPlays the timeline once each time it changes.
keyframes (required)(value: Value) => readonly KeyframeTrack<Value>[] | readonly Keyframe<Value>[]Tracks, or keyframes for the whole value.
(value: Value) => readonly KeyframeTrack<Value>[] | readonly Keyframe<Value>[]Tracks, or keyframes for the whole value.
children (required)(value: Value) => ReactNodeThe content closure.
(value: Value) => ReactNodeThe content closure.
<One.iOS.KeyframeAnimator
initialValue={{ scale: 1, lift: 0 }}
trigger={count}
keyframes={() => [
One.iOS.KeyframeTrack('scale', [
One.iOS.LinearKeyframe(1.2, { duration: 0.2 }),
One.iOS.SpringKeyframe(1, { spring: 'bouncy' }),
]),
One.iOS.KeyframeTrack('lift', [
One.iOS.CubicKeyframe(-30, { duration: 0.3 }),
One.iOS.MoveKeyframe(0),
]),
]}
>
{(value) => (
<One.iOS.Text
text="Done"
swiftStyle={{ scaleEffectWithSAndAnchor: { s: value.scale, anchor: 'center' }, offset: { x: 0, y: value.lift } }}
/>
)}
</One.iOS.KeyframeAnimator>

KeyframeTrack(keyPath, keyframes) animates one field; KeyframeTrack(keyframes) or a bare list of keyframes animates the whole value. A field takes one track.

KeyframeSwiftUIOptions
LinearKeyframe(to, options)LinearKeyframeduration, timingCurve (a UnitCurve name or { startControlPoint, endControlPoint })
SpringKeyframe(to, options)SpringKeyframeduration, spring (smooth, snappy, bouncy or a Spring initializer), startVelocity
CubicKeyframe(to, options)CubicKeyframeduration, startVelocity, endVelocity
MoveKeyframe(to)MoveKeyframenone

SwiftUI calls the content closure every frame, which JavaScript cannot do without a frame loop. Instead the closure runs during render at the initial value and at one step per field, and each swiftStyle number must follow the value linearly, such as value.scale or 40 * value.width + 8. SwiftUI then computes every frame’s numbers natively. A closure whose numbers curve with the value, or whose strings or colors change with it, throws. The keyframes function must not read its argument.

Limits

Edit this page on GitHub.