ハイライト
SwiftUI のアニメーション システムは、ビューの更新 (属性グラフの更新)、アニメーション可能プロトコル (補間にどの属性が関与するかを決定)、アニメーション タイプ (補間曲線を定義)、およびトランザクション (アニメーション コンテキストの転送) の 4 つのコア メカニズムで構成されています。開発者は合格できる
withAnimation、.animation()モディファイアとカスタム TransactionKey はアニメーションの動作を正確に制御します。
主な内容
SwiftUI でアニメーションが重要な理由
SwiftUI チームの Kyle は、全体を通じてペット投票アプリを例として使用しています。このアプリでは、各ペットのアバターをクリックして投票することができ、投票数が変化するとアバターが並べ替えられます。アバターをクリックすると、アバターがズームインします。この単純なインタラクションは、アニメーションなしでは堅苦しくなりますが、アニメーションを使用するとすぐに鮮やかになります。
SwiftUI はアニメーションをシンプルにするように設計されています。アニメーションは UI に明瞭さと活力をもたらすことができ、SwiftUI の宣言型アーキテクチャは当然アニメーションに適しています。
ビューの更新の構造
(02:08) SwiftUI は長期的な依存関係グラフ (属性グラフ) を維持します。各ノードは属性と呼ばれ、UI の細かい部分に対応します。いつ@State依存関係が変更された場合:
- イベントがトリガーされ、更新トランザクションが開始されます。
- 依存関係が変更され、ビューが無効としてマークされます
- トランザクションが閉じられると、フレームワークは
body新しいビュー値を生成する - 属性の値はレイヤーごとに更新されます。
- グラフは描画コマンドを発行してレンダリングを更新します。
これは、SwiftUI ビューの更新の完全なフローです。このプロセスを理解することは、アニメーションを理解する上で非常に重要です。
アニメーションを追加する
(04:13)withAnimationパッケージのステータスの変更:
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:35)Animatableプロトコルは「どのプロパティにアニメーション補間が必要か」を決定します。このプロトコルに準拠する型は宣言する必要があります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 アニメーションはいくつかのカテゴリに分類されます。
タイミング カーブ アニメーション: カーブと期間で定義されます。のようにeaseInOut、linear、easeIn、easeOut。UnitCurveタイプは、任意の時点での値と速度を個別に計算できます。
スプリング アニメーション: スプリング シミュレーションを通じて値を計算します。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:25)CustomAnimationこのプロトコルは、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:56)Spring型は独立して使用できます。
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 つの内蔵プリセット:
smooth、snappy、bouncy
UnitCurve
(12:48)UnitCurveアニメーションの速度曲線を定義します。
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)
キーポイント:
- ベジェ制御点を使用して曲線を定義する
- 任意の相対時点での値と速度を計算できます
- 内蔵プリセット:
linear、easeIn、easeOut、easeInOut
重要ポイント
- インタラクティブなフィードバックアニメーション
- やるべきこと: ボタンやカードなどのインタラクティブな要素にスケーリングまたはシャドウ アニメーションを追加します。
- 実行する価値がある理由: SwiftUI
.animation(.bouncy, value:)これにより実装が非常にシンプルになり、デフォルトで物理的なスプリング アニメーションが使用されるため、自然な感触が得られます。 - 開始方法: で
onTapGestureの間で切り替えます@State、使用.animation(.bouncy, value: isPressed)ターゲットビューを変更する
- カスタム レイアウト アニメーション
- 何をするか: 特定のパスに沿ってカスタム レイアウトでサブビューをアニメーション化します (円形の配置、グリッドの再配置など)。
- 実行する価値がある理由: フォローする
Animatableプロトコルの後、SwiftUI はすべてのフレームを呼び出しますbodyそして補間されたデータを渡します - 始め方:カスタマイズしてみよう
LayoutまたはViewフォローするAnimatable、声明animatableData、存在するbodyこの値に基づいてサブビューの位置を計算します
- 差別化されたアニメーション効果
- 機能: 同じビューの異なるプロパティに異なるアニメーション カーブを使用します。
- 実行する価値がある理由: シェーディングのため
.smooth見た目も安定していてズームにも使える.bouncy組み合わせると生き生きとした印象になり、レイヤーが豊かになります。 - 開始方法: 複数を積み重ねる
.animation()修飾子。それぞれ異なる修飾子を指定しますvalueパラメータ
- プログラムによってトリガーされるアニメーション
- 実行内容: 外部からビュー アニメーションをトリガーする (ネットワーク リクエストの完了、タイマーなど)
- 実行する価値がある理由: 使用する
withAnimationパッケージのステータスが変更され、ユーザーの操作なしでアニメーションを再生できます - 始め方:ウィル
@Stateに変更します@Binding、外部パスwithAnimation(.spring) { selected = true }トリガー
- トランザクションはカスタム コンテキストを渡します
- 対処法: 異なるアニメーション カーブを使用して、「ユーザーのクリックによってトリガーされるアニメーション」と「プログラムによってトリガーされるアニメーション」を区別します。
- 実行する価値がある理由: ユーザーの対話がより活発になり、プログラムの変更がより抑制されます。
- 始め方: 定義
TransactionKey、クリック時に使用されますwithTransactionにマークを設定します.transactionモディファイアのマーカーに基づいてアニメーションを選択
関連セッション
- スプリングを使用したアニメーション — スプリング アニメーションの原理と使用法についての深い理解
- SwiftUI で高度なアニメーションを使いこなす — マルチステップ アニメーションとキーフレーム アニメーション
- SwiftUI の要点 — SwiftUI の基本概念
- スクロール ビューを超えて — ScrollView のアニメーションとトランジション
コメント
GitHub Issues · utterances