いろは3019021 views
小学理科720872 views
Computer369216 views
高校化学2928968 views
中学数学624513 views
中学社会669542 views
LaTeX963955 views
数学講師2900085 views
高校倫理1442459 views
小学社会311093 views
Help
Tools

English

SwiftUI カスタム Transition の作成

組み込みの Transition だけでは表現できない動きが必要な場合、カスタム Transition を作成できる。ViewModifier を 2 つ用意し、それらの間を補間することで独自のアニメーション効果を実現する。

AnyTransition.modifier の基本

カスタム Transition は、active 状態と identity 状態の 2 つの ViewModifier を定義して作る。

struct RotateModifier: ViewModifier {
    let angle: Double
    let anchor: UnitPoint
    
    func body(content: Content) -> some View {
        content
            .rotationEffect(.degrees(angle), anchor: anchor)
            .opacity(angle == 0 ? 1 : 0)
    }
}

extension AnyTransition {
    static var rotate: AnyTransition {
        .modifier(
            active: RotateModifier(angle: 90, anchor: .bottomLeading),
            identity: RotateModifier(angle: 0, anchor: .bottomLeading)
        )
    }
}

active は「ビューがまだ見えない状態」、identity は「ビューが完全に見えている状態」を表す。挿入時は active から identity へ、削除時は identity から active へアニメーションする。

Text("回転して登場")
    .transition(.rotate)

より実践的な例:フリップ Transition

カードがめくれるような効果を作ってみる。

struct FlipModifier: ViewModifier {
    let angle: Double
    let axis: (x: CGFloat, y: CGFloat, z: CGFloat)
    
    func body(content: Content) -> some View {
        content
            .rotation3DEffect(
                .degrees(angle),
                axis: axis,
                perspective: 0.5
            )
            .opacity(abs(angle) > 90 ? 0 : 1)
    }
}

extension AnyTransition {
    static var flipFromLeft: AnyTransition {
        .modifier(
            active: FlipModifier(angle: -180, axis: (0, 1, 0)),
            identity: FlipModifier(angle: 0, axis: (0, 1, 0))
        )
    }
    
    static var flipFromBottom: AnyTransition {
        .modifier(
            active: FlipModifier(angle: 90, axis: (1, 0, 0)),
            identity: FlipModifier(angle: 0, axis: (1, 0, 0))
        )
    }
}

3D 回転を使うことで、平面的なスライドやフェードとは違った奥行きのある動きを実現できる。

asymmetric と組み合わせる

カスタム Transition も asymmetric と組み合わせて、挿入と削除で異なる動きにできる。

extension AnyTransition {
    static var rotateInOut: AnyTransition {
        .asymmetric(
            insertion: .modifier(
                active: RotateModifier(angle: -90, anchor: .topTrailing),
                identity: RotateModifier(angle: 0, anchor: .topTrailing)
            ),
            removal: .modifier(
                active: RotateModifier(angle: 90, anchor: .bottomLeading),
                identity: RotateModifier(angle: 0, anchor: .bottomLeading)
            )
        )
    }
}

挿入時は右上を軸に回転して登場し、削除時は左下を軸に回転して消えていく。

Animatable プロトコルとの連携

より複雑なアニメーションを実現するには、ViewModifier に Animatable プロトコルを実装する。

struct WaveModifier: ViewModifier, Animatable {
    var progress: Double
    
    var animatableData: Double {
        get { progress }
        set { progress = newValue }
    }
    
    func body(content: Content) -> some View {
        content
            .offset(y: sin(progress * .pi * 2) * 10)
            .scaleEffect(1 + cos(progress * .pi) * 0.1)
    }
}

animatableData を定義することで、SwiftUI がその値を補間してくれる。これにより、単純な線形補間では表現できない複雑な動きも可能になる。

注意点とベストプラクティス

パフォーマンス

カスタム Transition は描画負荷が高くなりがち。3D 回転や複雑なエフェクトは、多数の要素に同時適用しないよう注意する。

一貫性

アプリ内で使う Transition は統一感を持たせる。あちこちで異なる派手な Transition を使うと、ユーザーが混乱する原因になる。

テスト

シミュレーターだけでなく実機でも確認する。特に古いデバイスではパフォーマンスの問題が顕著に現れることがある。

カスタム Transition は表現の幅を広げる強力なツールだが、使いすぎには注意したい。標準の Transition で十分な場面も多いので、本当に必要な場面で効果的に使うことが大切だ。