中学数学621382 views
中学英語808712 views
英語607877 views
小学理科717236 views
小学算数1194618 views
Computer365120 views
高校生物549842 views
LaTeX957300 views
高校日本史189857 views
高校倫理1433119 views
Help
Tools

English

SwiftUI animation モディファイアと暗黙的アニメーション

SwiftUI には .animation モディファイアを使った暗黙的アニメーションという仕組みがある。withAnimation が「このタイミングで」と明示するのに対し、暗黙的アニメーションは「この値が変わったら常に」という宣言的なアプローチだ。

animation モディファイアの基本

.animation モディファイアは、特定の値の変化を監視し、その値に依存するビューの変更をアニメーション化する。

struct ContentView: View {
    @State private var isExpanded = false
    
    var body: some View {
        VStack {
            Rectangle()
                .fill(.blue)
                .frame(width: isExpanded ? 200 : 100,
                       height: isExpanded ? 200 : 100)
                .animation(.easeInOut, value: isExpanded)
            
            Button("Toggle") {
                isExpanded.toggle()
            }
        }
    }
}

withAnimation で囲まなくても、isExpanded が変わるたびに自動でアニメーションが実行される。

value パラメータの重要性

iOS 15 以降、.animation には必ず value パラメータを指定する必要がある。これは「どの値の変化に反応するか」を明示するためだ。

// iOS 15 以降の正しい書き方
.animation(.spring(), value: someValue)

// 非推奨(iOS 15 で deprecated)
.animation(.spring())

value を省略した古い書き方は、意図しないアニメーションが発生する原因になっていた。どの状態変化に対してアニメーションするかを明確にすることで、予測可能な動作が保証される。

複数の値を監視

複数の値の変化に対してアニメーションさせたい場合は、animation モディファイアを複数つけるか、値をまとめる。

struct MultiValueView: View {
    @State private var offset: CGFloat = 0
    @State private var rotation: Double = 0
    
    var body: some View {
        Rectangle()
            .frame(width: 100, height: 100)
            .offset(y: offset)
            .rotationEffect(.degrees(rotation))
            .animation(.spring(), value: offset)
            .animation(.easeInOut, value: rotation)
    }
}

offset の変化にはスプリング、rotation の変化には easeInOut と、別々のアニメーションを適用できる。

暗黙的 vs 明示的アニメーション

暗黙的アニメーション(.animation)

ビューの定義時にアニメーションを宣言。値が変われば常にアニメーション。宣言的で SwiftUI らしいアプローチ。

明示的アニメーション(withAnimation)

状態変更時にアニメーションを指定。特定のアクションだけアニメーションさせたい場合に有効。手続き的なアプローチ。

どちらを使うかは設計思想による。「このビューは常にアニメーションする」なら暗黙的、「このボタンを押したときだけ」なら明示的が適している。

animation(nil) でアニメーションを無効化

特定の変更だけアニメーションさせたくない場合、.animation(nil, value:) を使う。

struct SelectiveAnimationView: View {
    @State private var position: CGFloat = 0
    @State private var color: Color = .blue
    
    var body: some View {
        Rectangle()
            .fill(color)
            .frame(width: 100, height: 100)
            .offset(x: position)
            .animation(.spring(), value: position)
            .animation(nil, value: color)  // 色の変化はアニメーションしない
    }
}

これにより、位置の変化はアニメーションするが、色の変化は即座に反映される。

適用順序の注意点

animation モディファイアは、それより上に書かれたモディファイアにのみ影響する。

Rectangle()
    .fill(color)
    .animation(.easeInOut, value: color)  // fill に影響
    .frame(width: size, height: size)
    .animation(.spring(), value: size)     // frame に影響

モディファイアチェーンの順序を意識することで、部分的なアニメーション制御が可能になる。このあたりは SwiftUI のモディファイア適用順序の理解が前提となる。