SwiftUI スプリングアニメーション詳細

スプリングアニメーションは SwiftUI で最も自然な動きを表現できるアニメーションだ。物理的なバネの動きをシミュレートし、跳ね返りや減衰を伴う有機的な動きを実現する。

spring() の基本

最もシンプルな形は引数なしの .spring() を使うこと。

withAnimation(.spring()) {
    isExpanded.toggle()
}

デフォルトのスプリングは適度な跳ね返りを持ち、多くの場面で自然に見える。

従来のスプリングパラメータ

iOS 16 以前では、4 つの物理パラメータでスプリングを定義していた。

Animation.spring(
    response: 0.5,
    dampingFraction: 0.6,
    blendDuration: 0.0
)
responseバネが 1 周期振動する時間(秒)。小さいほど速い
dampingFraction減衰率。0 で永久振動、1 で臨界減衰(跳ねない)
blendDuration他アニメーションとブレンドする時間

dampingFraction が鍵で、0.5〜0.7 あたりが心地よい跳ね返りを生む。1.0 以上にすると跳ね返りがなくなり、滑らかに停止する。

interactiveSpring

ドラッグ操作など、ユーザーのインタラクションに追従するアニメーションには interactiveSpring が適している。

Animation.interactiveSpring(
    response: 0.3,
    dampingFraction: 0.8,
    blendDuration: 0.25
)

response を短く、dampingFraction を高めに設定することで、指の動きに素早く反応しつつ、離したときにスムーズに落ち着く動きになる。

iOS 17 の新しいスプリング API

iOS 17 では、より直感的なパラメータでスプリングを定義できるようになった。

// duration と bounce で指定
Animation.spring(duration: 0.5, bounce: 0.3)

// Spring 型を使う
let spring = Spring(duration: 0.5, bounce: 0.4)
Animation.spring(spring)
durationアニメーションのおおよその時間
bounce跳ね返りの強さ(0〜1、0 で跳ねない)

従来の物理パラメータより「どんな動きにしたいか」を直接表現できる。

Spring 型の詳細設定

Spring 型には従来の物理パラメータも設定できる。

// 物理パラメータで初期化
let spring = Spring(
    mass: 1.0,
    stiffness: 100,
    damping: 10
)

// 減衰率で初期化
let spring = Spring(
    mass: 1.0,
    stiffness: 200,
    damping: Spring.initialDamping(
        for: 0.7  // dampingRatio
    )
)
massバネにつながった物体の質量。大きいほど動きが重くなる
stiffnessバネの硬さ。大きいほど素早く動く
damping減衰係数。大きいほど早く止まる

これらのパラメータは物理シミュレーションに基づいているため、変更すると連動して動きが変わる。

スプリングアニメーションの使い分け

UI フィードバック(ボタン、トグル)

bounce: 0.2〜0.3、duration: 0.3 程度。控えめな跳ね返りで素早く反応。

モーダル表示

bounce: 0.4〜0.5、duration: 0.5 程度。存在感のある動きでユーザーの注目を集める。

ドラッグ&ドロップ

interactiveSpring で dampingFraction: 0.8 以上。指に追従しつつ、離すと素早く定位置に戻る。

スプリングの duration について

スプリングアニメーションは「バネが停止するまで」続くため、厳密な終了時間がない。duration パラメータは「だいたいこのくらい」という目安であり、bounce が大きいと実際の時間は長くなる。

// bounce が大きいと duration より長くなる
Animation.spring(duration: 0.3, bounce: 0.8)

完了タイミングが重要な場面では、completionCriteria に .logicallyComplete を指定して、主要な動きが終わった時点でコールバックを受け取るとよい。