SwiftUI KeyframeAnimator(iOS 17)
KeyframeAnimator は iOS 17 で追加された、キーフレームベースのアニメーションを実現する機能だ。アニメーションの各時点における値を明示的に指定でき、複雑な動きを細かく制御できる。
KeyframeAnimator の基本構造
KeyframeAnimator は、アニメーションさせる値の構造体と、その値を時間軸に沿って定義するキーフレームで構成される。
struct AnimationValues {
var scale: CGFloat = 1.0
var rotation: Double = 0.0
var yOffset: CGFloat = 0.0
}
struct BouncingShape: View {
var body: some View {
KeyframeAnimator(
initialValue: AnimationValues()
) { values in
Circle()
.fill(.blue)
.frame(width: 100, height: 100)
.scaleEffect(values.scale)
.rotationEffect(.degrees(values.rotation))
.offset(y: values.yOffset)
} keyframes: { _ in
KeyframeTrack(\.scale) {
LinearKeyframe(1.2, duration: 0.2)
SpringKeyframe(1.0, duration: 0.3)
}
KeyframeTrack(\.yOffset) {
LinearKeyframe(-50, duration: 0.15)
SpringKeyframe(0, duration: 0.5)
}
}
}
}KeyframeTrack で各プロパティのキーフレームを定義し、時間に沿った値の変化を指定する。
キーフレームの種類
SwiftUI には 4 種類のキーフレームが用意されている。
KeyframeTrack(\.scale) {
LinearKeyframe(1.5, duration: 0.3) // 線形補間
SpringKeyframe(1.0, duration: 0.5) // スプリング補間
CubicKeyframe(0.8, duration: 0.2) // ベジェ曲線補間
MoveKeyframe(1.2) // 即座に移動(補間なし)
}| LinearKeyframe | 一定速度で直線的に変化 |
| SpringKeyframe | バネのような動きで変化 |
| CubicKeyframe | なめらかな曲線で変化 |
| MoveKeyframe | アニメーションなしで即座に値を変更 |
trigger でアニメーション開始を制御
trigger パラメータを使えば、値が変化したときにアニメーションを開始できる。
struct TriggeredKeyframe: View {
@State private var trigger = false
var body: some View {
VStack {
KeyframeAnimator(
initialValue: AnimationValues(),
trigger: trigger
) { values in
RoundedRectangle(cornerRadius: 20)
.fill(.purple)
.frame(width: 100, height: 100)
.scaleEffect(values.scale)
.rotationEffect(.degrees(values.rotation))
} keyframes: { _ in
KeyframeTrack(\.scale) {
LinearKeyframe(1.3, duration: 0.1)
LinearKeyframe(0.9, duration: 0.1)
SpringKeyframe(1.0, duration: 0.3)
}
KeyframeTrack(\.rotation) {
LinearKeyframe(-10, duration: 0.1)
LinearKeyframe(10, duration: 0.1)
SpringKeyframe(0, duration: 0.2)
}
}
Button("Animate") {
trigger.toggle()
}
}
}
}ボタンを押すたびに、シェイク風のアニメーションが再生される。
repeatForever との組み合わせ
キーフレームアニメーションを繰り返すには、repeating パラメータを使う。
KeyframeAnimator(
initialValue: AnimationValues(),
repeating: true
) { values in
// ビュー
} keyframes: { _ in
// キーフレーム
}全てのキーフレームが終了すると、最初から再び開始する。
実践例:ロケット発射アニメーション
struct RocketValues {
var yOffset: CGFloat = 0
var scale: CGFloat = 1.0
var rotation: Double = 0
var opacity: Double = 1.0
}
struct RocketLaunch: View {
@State private var launch = false
var body: some View {
VStack {
KeyframeAnimator(
initialValue: RocketValues(),
trigger: launch
) { values in
Text("🚀")
.font(.system(size: 60))
.offset(y: values.yOffset)
.scaleEffect(values.scale)
.rotationEffect(.degrees(values.rotation))
.opacity(values.opacity)
} keyframes: { _ in
KeyframeTrack(\.yOffset) {
// カウントダウン中は少し震える
LinearKeyframe(5, duration: 0.1)
LinearKeyframe(-5, duration: 0.1)
LinearKeyframe(3, duration: 0.1)
LinearKeyframe(0, duration: 0.1)
// 発射
CubicKeyframe(-400, duration: 0.8)
}
KeyframeTrack(\.scale) {
LinearKeyframe(1.0, duration: 0.4)
LinearKeyframe(1.2, duration: 0.2)
LinearKeyframe(0.3, duration: 0.6)
}
KeyframeTrack(\.rotation) {
LinearKeyframe(-5, duration: 0.1)
LinearKeyframe(5, duration: 0.1)
LinearKeyframe(0, duration: 0.2)
LinearKeyframe(0, duration: 0.8)
}
}
Button("発射") {
launch.toggle()
}
}
}
}震え、発射、縮小が組み合わさった複雑なアニメーションが、キーフレームで直感的に定義できる。
PhaseAnimator との違い
KeyframeAnimator
時間軸に沿った正確な値の変化を指定。アニメーションの「形」を細かく制御したい場合に最適。
PhaseAnimator
離散的なフェーズを定義し、その間を自動補間。単純な状態遷移やループアニメーションに向く。
KeyframeAnimator は、アニメーションデザイナーが After Effects などで作るような「キーフレームアニメーション」を SwiftUI で再現するための機能だ。細かい動きの調整が必要な場面で威力を発揮する。