LaTeX957300 views
高校日本史189857 views
高校生物549842 views
小学算数1194618 views
高校物理158224 views
高校化学2913383 views
小学理科717236 views
高校国語785655 views
雑学1472593 views
世界の国560595 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 で十分な場面も多いので、本当に必要な場面で効果的に使うことが大切だ。