WWDC Quick Look 💓 By SwiftGGTeam
Explore SwiftUI animation

Explore SwiftUI animation

元の動画を見る

ハイライト

SwiftUI のアニメーション システムは、ビューの更新 (属性グラフの更新)、アニメーション可能プロトコル (補間にどの属性が関与するかを決定)、アニメーション タイプ (補間曲線を定義)、およびトランザクション (アニメーション コンテキストの転送) の 4 つのコア メカニズムで構成されています。開発者は合格できるwithAnimation.animation()モディファイアとカスタム TransactionKey はアニメーションの動作を正確に制御します。

主な内容

SwiftUI でアニメーションが重要な理由

SwiftUI チームの Kyle は、全体を通じてペット投票アプリを例として使用しています。このアプリでは、各ペットのアバターをクリックして投票することができ、投票数が変化するとアバターが並べ替えられます。アバターをクリックすると、アバターがズームインします。この単純なインタラクションは、アニメーションなしでは堅苦しくなりますが、アニメーションを使用するとすぐに鮮やかになります。

SwiftUI はアニメーションをシンプルにするように設計されています。アニメーションは UI に明瞭さと活力をもたらすことができ、SwiftUI の宣言型アーキテクチャは当然アニメーションに適しています。

ビューの更新の構造

(02:08) SwiftUI は長期的な依存関係グラフ (属性グラフ) を維持します。各ノードは属性と呼ばれ、UI の細かい部分に対応します。いつ@State依存関係が変更された場合:

  1. イベントがトリガーされ、更新トランザクションが開始されます。
  2. 依存関係が変更され、ビューが無効としてマークされます
  3. トランザクションが閉じられると、フレームワークはbody新しいビュー値を生成する
  4. 属性の値はレイヤーごとに更新されます。
  5. グラフは描画コマンドを発行してレンダリングを更新します。

これは、SwiftUI ビューの更新の完全なフローです。このプロセスを理解することは、アニメーションを理解する上で非常に重要です。

アニメーションを追加する

04:13withAnimationパッケージのステータスの変更:

struct Avatar: View {
    var pet: Pet
    @State private var selected: Bool = false

    var body: some View {
        Image(pet.type)
            .scaleEffect(selected ? 1.5 : 1.0)
            .onTapGesture {
                withAnimation {
                    selected.toggle()
                }
            }
    }
}

キーポイント:

  • withAnimationトランザクション内でのアニメーション化
  • selected変更後、ダウンストリーム属性は無効としてマークされます
  • body新しい属性値を生成するために呼び出されます
  • scaleEffectこれはアニメーション化可能な属性です。値の変更が検出され、トランザクションにアニメーションがある場合、アニメーションはコピーされて補間されます。
  • SwiftUI は、ビュー コードを呼び出すことなく、バックグラウンド スレッドで補間計算を完了します。

アニメーション化可能なプロトコル

06:35Animatableプロトコルは「どのプロパティにアニメーション補間が必要か」を決定します。このプロトコルに準拠する型は宣言する必要がありますanimatableData属性と型が後に続く必要がありますVectorArithmetic

VectorArithmeticベクトル加算とスカラー乗算がサポートされています。CGFloatそしてDoubleは 1 次元ベクトルであり、CGPointそしてCGSizeは 2 次元ベクトルであり、CGRectは 4 次元ベクトルです。SwiftUI は、統合された汎用実装を使用して、これらすべてのタイプをアニメーション化します。

scaleEffect実際には、幅のスケーリング、高さのスケーリング、アンカー ポイント x、アンカー ポイント y の 4 次元ベクトルが定義されます。使用AnimatablePair複数のベクトルをより大きなベクトルに連結します。

(09:00) カスタムAnimatable例: ペット表彰台ビューでは次のように使用されます。RadialLayoutサブビューを円弧に沿って配置します。デフォルトでは、オフセット角度を変更すると、ペットの肖像画は直線的に移動します。PodiumをフォローしてもらうことでAnimatableオフセット角度を次のように使用しますanimatableDataこれにより、アバターが円弧に沿って移動できるようになります。

ただし注意してください: カスタマイズAnimatableフレームごとに呼び出されますbody、内蔵エフェクトよりもはるかに高価です。内蔵エフェクトではこれが不可能な場合にのみ使用されます。

アニメーションの種類

(12:16) SwiftUI アニメーションはいくつかのカテゴリに分類されます。

タイミング カーブ アニメーション: カーブと期間で定義されます。のようにeaseInOutlineareaseIneaseOutUnitCurveタイプは、任意の時点での値と速度を個別に計算できます。

スプリング アニメーション: スプリング シミュレーションを通じて値を計算します。iOS17からは、withAnimationデフォルトのアニメーションは、easeInOutになったsmooth春。3 つの内蔵プリセット:smooth(バウンスなし)、snappy(少量のバウンス)、bouncy(さらに跳ね返ります)。

高次アニメーション: 減速、加速、遅延、繰り返しなどの基本的なアニメーションを変更します。

カスタム アニメーション: 合格CustomAnimationこのプロトコルはカスタム アニメーション アルゴリズムを実装します。

Transaction

(19:57) トランザクションは、環境や設定と同様に、SwiftUI が暗黙的に更新コンテキストを渡す辞書です。各ビューの更新にはトランザクションがあり、これはアニメーション設定を伝達するために最も一般的に使用されます。

withAnimationのみwithTransaction薄いパッケージ。更新が完了するとトランザクションは破棄されるため、すべての値はデフォルト値に復元されます。

22:44.animation()モディファイアを使用すると、アニメーション スコープを正確に制御できます。

Image(pet.type)
    .scaleEffect(selected ? 1.5 : 1.0)
    .animation(.bouncy, value: selected)

キーポイント:

  • でのみvalueアニメーションが変化したときのみトランザクションに書き込む
  • 無差別なアニメーション オーバーレイによる予期しないアニメーションを回避します

複数のアニメーション モディファイアをスタックできます。

Image(pet.type)
    .shadow(radius: selected ? 12 : 8)
    .animation(.smooth, value: selected)
    .scaleEffect(selected ? 1.5 : 1.0)
    .animation(.bouncy, value: selected)

(25:20) 任意の子コンテンツを含む汎用コンポーネントの場合は、次を使用します。body閉まっている.animation()アニメーションの範囲を指定されたアニメーション化可能属性に限定するモディファイア:

struct Avatar<Content: View>: View {
    var content: Content
    @Binding var selected: Bool

    var body: some View {
        content
            .animation(.smooth) {
                $0.shadow(radius: selected ? 12 : 8)
            }
            .animation(.bouncy) {
                $0.scaleEffect(selected ? 1.5 : 1.0)
            }
            .onTapGesture {
                selected.toggle()
            }
    }
}

(28:45) Custom TransactionKey はトランザクションでカスタム データを渡すことができます。

private struct AvatarTappedKey: TransactionKey {
    static let defaultValue = false
}

extension Transaction {
    var avatarTapped: Bool {
        get { self[AvatarTappedKey.self] }
        set { self[AvatarTappedKey.self] = newValue }
    }
}

Image(pet.type)
    .scaleEffect(selected ? 1.5 : 1.0)
    .transaction(value: selected) {
        $0.animation = $0.avatarTapped ? .bouncy : .smooth
    }
    .onTapGesture {
        withTransaction(\.avatarTapped, true) {
            selected.toggle()
        }
    }

キーポイント:

  • フォローする固有のタイプを作成するTransactionKey、供給defaultValue
  • 存在するTransaction拡張機能での計算プロパティの宣言
  • 使用withTransaction設定値、withAnimation薄いパッケージです
  • 新しいバージョン.transaction()修飾子もサポートされていますvalueパラメータとbody範囲を正確に制御するクロージャ

##詳細

カスタムアニメーション

17:25CustomAnimationこのプロトコルは、SwiftUI の組み込みアニメーションと同じ低レベルのエントリ ポイントを提供します。

struct MyLinearAnimation: CustomAnimation {
    var duration: TimeInterval

    func animate<V: VectorArithmetic>(
        value: V,
        time: TimeInterval,
        context: inout AnimationContext<V>
    ) -> V? {
        if time <= duration {
            value.scaled(by: time / duration)
        } else {
            nil // animation has finished
        }
    }
}

キーポイント:

  • animateターゲットベクトル、経過時間、コンテキストを受信します
  • 現在のアニメーション値を返す、またはnilアニメーションの終了を示します
  • 実際にアニメーションするのは、0から目標値までのデルタベクトル(古い値から新しい値への変化量)です。
  • 汎用実装のため、あらゆる次元のアニメーション化可能なデータをサポートします

(19:50) オプションvelocityアニメーションを結合するときに速度を維持する方法:

func velocity<V: VectorArithmetic>(
    value: V, time: TimeInterval, context: AnimationContext<V>
) -> V? {
    value.scaled(by: 1.0 / duration)
}

shouldMerge新しいアニメーションを実行中のアニメーションにマージするかどうかを決定します。タイミング カーブは false (オーバーレイの実行) を返し、スプリングは true (速度が保持され、再ターゲットされる) を返します。

春モデル

13:56Spring型は独立して使用できます。

let spring = Spring(duration: 1.0, bounce: 0)
spring.value(target: 1, time: 0.25)
spring.velocity(target: 1, time: 0.25)

キーポイント:

  • durationは知覚の持続時間であり、bounceバウンスレベルをコントロールする
  • いつでも位置と速度を計算できます
  • 3 つの内蔵プリセット:smoothsnappybouncy

UnitCurve

12:48UnitCurveアニメーションの速度曲線を定義します。

let curve = UnitCurve(
    startControlPoint: UnitPoint(x: 0.25, y: 0.1),
    endControlPoint: UnitPoint(x: 0.25, y: 1))
curve.value(at: 0.25)
curve.velocity(at: 0.25)

キーポイント:

  • ベジェ制御点を使用して曲線を定義する
  • 任意の相対時点での値と速度を計算できます
  • 内蔵プリセット:lineareaseIneaseOuteaseInOut

重要ポイント

  1. インタラクティブなフィードバックアニメーション
  • やるべきこと: ボタンやカードなどのインタラクティブな要素にスケーリングまたはシャドウ アニメーションを追加します。
  • 実行する価値がある理由: SwiftUI.animation(.bouncy, value:)これにより実装が非常にシンプルになり、デフォルトで物理的なスプリング アニメーションが使用されるため、自然な感触が得られます。
  • 開始方法: でonTapGestureの間で切り替えます@State、使用.animation(.bouncy, value: isPressed)ターゲットビューを変更する
  1. カスタム レイアウト アニメーション
  • 何をするか: 特定のパスに沿ってカスタム レイアウトでサブビューをアニメーション化します (円形の配置、グリッドの再配置など)。
  • 実行する価値がある理由: フォローするAnimatableプロトコルの後、SwiftUI はすべてのフレームを呼び出しますbodyそして補間されたデータを渡します
  • 始め方:カスタマイズしてみようLayoutまたはViewフォローするAnimatable、声明animatableData、存在するbodyこの値に基づいてサブビューの位置を計算します
  1. 差別化されたアニメーション効果
  • 機能: 同じビューの異なるプロパティに異なるアニメーション カーブを使用します。
  • 実行する価値がある理由: シェーディングのため.smooth見た目も安定していてズームにも使える.bouncy組み合わせると生き生きとした印象になり、レイヤーが豊かになります。
  • 開始方法: 複数を積み重ねる.animation()修飾子。それぞれ異なる修飾子を指定しますvalueパラメータ
  1. プログラムによってトリガーされるアニメーション
  • 実行内容: 外部からビュー アニメーションをトリガーする (ネットワーク リクエストの完了、タイマーなど)
  • 実行する価値がある理由: 使用するwithAnimationパッケージのステータスが変更され、ユーザーの操作なしでアニメーションを再生できます
  • 始め方:ウィル@Stateに変更します@Binding、外部パスwithAnimation(.spring) { selected = true }トリガー
  1. トランザクションはカスタム コンテキストを渡します
  • 対処法: 異なるアニメーション カーブを使用して、「ユーザーのクリックによってトリガーされるアニメーション」と「プログラムによってトリガーされるアニメーション」を区別します。
  • 実行する価値がある理由: ユーザーの対話がより活発になり、プログラムの変更がより抑制されます。
  • 始め方: 定義TransactionKey、クリック時に使用されますwithTransactionにマークを設定します.transactionモディファイアのマーカーに基づいてアニメーションを選択

関連セッション

コメント

GitHub Issues · utterances