TechnologiesSwiftUIPreviews and Layout Modifiers

AnimatableModifier protocol

iOSmacOStvOSwatchOSvisionOS✓ renders

A modifier that can create another modifier with animation.

How it works

AnimatableModifier is a view modifier protocol whose values SwiftUI can interpolate frame by frame, letting you animate effects that ordinary modifiers can't reach. A standard modifier only changes a view's appearance at fixed states; by conforming to AnimatableModifier you expose a continuous quantity through animatableData, and SwiftUI drives that value smoothly between its start and end. Reach for it when you need custom, parameterized motion — a shake, a wiggle, a counting number, a morphing path — that should respond to withAnimation like any built-in modifier. It combines the rendering of ViewModifier with the interpolation contract of Animatable in a single type.

  1. Conform a modifier to AnimatableModifier

    Declare a type that adopts AnimatableModifier, which inherits from both ViewModifier and Animatable. In the example, ShakeEffect is a struct conforming to AnimatableModifier, so SwiftUI treats it as a renderable modifier whose state it is also allowed to animate.

  2. Expose the animated quantity through animatableData

    The animatableData property is the single value SwiftUI reads and writes as an animation progresses; its type must be a VectorArithmetic such as CGFloat. ShakeEffect backs animatableData with its stored shakes property — its getter returns shakes and its setter assigns newValue — so each interpolated value flows straight back into the modifier.

  3. Render from the current value in body(content:)

    Implement body(content:) to build the modified view from content, the view the modifier is attached to. Because it is re-evaluated for every interpolated animatableData, reading the live value produces motion: here content.offset(x: sin(shakes * .pi * 2) * 10) shifts the view horizontally, and as shakes sweeps upward the sine term oscillates to create the side-to-side shake.

  4. Apply it with .modifier(_:)

    Attach the modifier to a view with the .modifier(_:) method, passing a configured instance. The example applies .modifier(ShakeEffect(shakes: shakes)) to the Text, wiring the modifier's value to a piece of view state so the effect tracks that state.

  5. Drive the value inside withAnimation

    Changing the bound value inside a withAnimation block is what tells SwiftUI to interpolate animatableData rather than jump. The Animate button calls withAnimation(.linear(duration: 0.6)) { shakes += 2 }, so @State shakes advances over time and SwiftUI re-runs body(content:) at each step to play the animation.

Try it — Increase the offset amplitude by changing * 10 in content.offset(x: sin(shakes * .pi * 2) * 10) to * 40 to see how animatableData scales the motion it drives.

Example & preview

Press Run live & edit to compile it in your browser — then edit the Swift on the left and the preview re-renders live.

AnimatableModifier.swift
struct AnimatableModifierDemo: View {
    struct ShakeEffect: AnimatableModifier {
        var shakes: CGFloat
        var animatableData: CGFloat {
            get { shakes }
            set { shakes = newValue }
        }
        func body(content: Content) -> some View {
            content.offset(x: sin(shakes * .pi * 2) * 10)
        }
    }

    @State private var shakes: CGFloat = 0

    var body: some View {
        VStack(spacing: 24) {
            Text("Shake me!")
                .font(.title2)
                .padding()
                .background(Color.blue.opacity(0.2))
                .cornerRadius(12)
                .modifier(ShakeEffect(shakes: shakes))

            Button("Animate") {
                withAnimation(.linear(duration: 0.6)) {
                    shakes += 2
                }
            }
        }
        .padding()
    }
}
Live preview
Shake me! Animate
Shake me! Animate
swift → lexer → parser → sema → uiir → canvas Open in Studio ↗
What's new in SwiftUI 27 →