Contents

CustomAnimation

A type that defines how an animatable value changes over time.

Declaration

@preconcurrency protocol CustomAnimation : Hashable, Sendable

Overview

Use this protocol to create a type that changes an animatable value over time, which produces a custom visual transition of a view. For example, the follow code changes an animatable value using an elastic ease-in ease-out function:

struct ElasticEaseInEaseOutAnimation: CustomAnimation {
    let duration: TimeInterval

    func animate<V>(value: V, time: TimeInterval, context: inout AnimationContext<V>) -> V? where V : VectorArithmetic {
        if time > duration { return nil } // The animation has finished.

        let p = time / duration
        let s = sin((20 * p - 11.125) * ((2 * Double.pi) / 4.5))
        if p < 0.5 {
            return value.scaled(by: -(pow(2, 20 * p - 10) * s) / 2)
        } else {
            return value.scaled(by: (pow(2, -20 * p + 10) * s) / 2 + 1)
        }
    }
}

To create an Animation instance of a custom animation, use the init(_:) initializer, passing in an instance of a custom animation; for example:

Animation(ElasticEaseInEaseOutAnimation(duration: 5.0))

To help make view code more readable, extend Animation and add a static property and function that returns an Animation instance of a custom animation. For example, the following code adds the static property elasticEaseInEaseOut that returns the elastic ease-in ease-out animation with a default duration of 0.35 seconds. Next, the code adds a method that returns the animation with a specified duration.

extension Animation {
    static var elasticEaseInEaseOut: Animation { elasticEaseInEaseOut(duration: 0.35) }
    static func elasticEaseInEaseOut(duration: TimeInterval) -> Animation {
        Animation(ElasticEaseInEaseOutAnimation(duration: duration))
    }
}

To animate a view with the elastic ease-in ease-out animation, a view calls either .elasticEaseInEaseOut or .elasticEaseInEaseOut(duration:). For example, the follow code includes an Animate button that, when clicked, animates a circle as it moves from one edge of the view to the other, using the elastic ease-in ease-out animation with a duration of 5 seconds:

struct ElasticEaseInEaseOutView: View {
    @State private var isActive = false

    var body: some View {
        VStack(alignment: isActive ? .trailing : .leading) {
            Circle()
                .frame(width: 100.0)
                .foregroundColor(.accentColor)

            Button("Animate") {
                withAnimation(.elasticEaseInEaseOut(duration: 5.0)) {
                    isActive.toggle()
                }
            }
            .frame(maxWidth: .infinity)
        }
        .padding()
    }
}

Topics

Animating a value

Getting the velocity

Determining whether to merge

See Also

Creating custom animations