【Swift/UIKit】CABasicAnimationの使い方!CAAnimationDelegate
この記事からわかること
- Swift/UIKitでUIViewをアニメーションさせる方法
- Core Animationフレームワークの役割
- CABasicAnimationの使い方と違い
- CAAnimationDelegateでアニメーションの開始と終了を検知する
- 無限回転するアニメーションの実装方法
index
[open]
\ アプリをリリースしました /
友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-
posted withアプリーチ
環境
- Xcode:15.0.1
- iOS:17.0
- Swift:5.9
- macOS:Sonoma 14.1
UIKitを使用したiOSアプリ開発でビューにアニメーションを実装する方法をまとめて行きます。
アニメーションを実装する方法
アニメーションを実装する方法はいくつか用意されています。それぞれの役割とは以下の通りになります。
- UIView.animate:サイズや色合いなど簡素なアニメーション
- UIView.transition:遷移アニメーション
- UIView.animateKeyframes:連続したアニメーション
- CABasicAnimation:複雑なアニメーション
今回はCore Animationフレームワークから提供されているCABasicAnimationの使い方をまとめていきます。
Core Animationフレームワークとは?
公式リファレンス:Core Animation
公式リファレンス:Core Animation Programming Guide
Core Animationフレームワークとはグラフィックレンダリングとアニメーションを提供するiOSとMac OS共通のフレームワークです。直接使用する機会は少ないですがiOSアプリにとってもインフラ部分になる大事なクラスです。
描画に特化しているのでイベント処理などを持たせることはできませんがその分描画速度が速く、GPUが処理を負担するためCPUへの負担も少ないのが大きな特徴になっています。
描画をGPUが担当するためBitmap形式でのデータ保存が必要になります。Core AnimationではBitmap形式でコンテンツを取り扱い、それをキャッシュ(「バッキングストア(backing store)」と呼ぶ)することで高速な描画を実現しています。
※Bitmapとは画像のデータ形式の1つでデータをピクセルの位置(座標)と色で管理する形式です。
まとめると「コンテンツを画像で管理することでGPUに処理を任せて高速に描画するためのフレームワーク」という感じでしょうか?
そしてCore Animationで肝になってくるのがCALayer(レイヤーオブジェクト)です。詳細に関しては以下の記事を参考にしてください。
CABasicAnimationの使い方
CABasicAnimation
クラスはCore Animationフレームワークから提供されているアニメーションを実装するためのクラスです。こちらはCALayer(レイヤーオブジェクト)に対してアニメーションを付与していくのでUIView
を直接操作していたUIView.animate
メソッドなどでアニメーションできなかったプロパティやより複雑な変化をさせることができます。
では先に実装方法を見てみます。まずは「透明度を0に、角を丸くするアニメーション」を付与してみます。
let square = UIView(frame: CGRect(x: 0, y:0, width: 150 , height: 150))
square.backgroundColor = .orange
square.center = self.view.center
// 透明度アニメーションの実装
let opacityAnimation = CABasicAnimation(keyPath: "opacity")
opacityAnimation.fromValue = 1
opacityAnimation.toValue = 0
opacityAnimation.duration = 2.0
square.layer.add(opacityAnimation, forKey: "opacityAnimation")
// 角丸アニメーションの実装
let cornerRadiusAnimation = CABasicAnimation(keyPath: "cornerRadius")
cornerRadiusAnimation.fromValue = 0
cornerRadiusAnimation.toValue = 30
cornerRadiusAnimation.duration = 2.0
square.layer.add(cornerRadiusAnimation, forKey: "cornerRadiusAnimation")
self.view.addSubview(square)
使い方
アニメーションを実装するにはCABasicAnimation
インスタンスを生成します。イニシャライザの引数keyPath
に渡すのは適当な文字列ではなく、プロパティのキーパス(名前)です。キーパスなので例えばスケールのX軸をアニメーションする場合はtransform.scale.x
を、回転させたい場合はtransform.rotation
を指定します。
let opacityAnimation = CABasicAnimation(keyPath: "opacity")
インスタンス化できたCABasicAnimation
に対してアニメーションの設定をしていきます。fromValue
プロパティでアニメーション開始時の値をtoValue
でアニメーション終了時の値を、duration
でアニメーションの継続時間を指定します。
// 開始の値
opacityAnimation.fromValue = 1
// 終了の値
opacityAnimation.toValue = 0
// アニメーション継続時間
opacityAnimation.duration = 2.0
最後にlayer(CALayer)
プロパティにadd
メソッドを使用してアニメーションを追加すればビューが描画されたタイミングでアニメーションが発火します。forKey
にはアニメーションを識別するための文字列を渡します。
square.layer.add(opacityAnimation, forKey: "opacityAnimation")
開始時間を遅らせる
アニメーションの開始時間を遅らせたい場合はbeginTime
プロパティに遅らせたい時間を渡します。 CACurrentMediaTime() + 1
で1秒後にアニメーションを開始することができます。
opacityAnimation.beginTime = CACurrentMediaTime() + 1
逆再生
アニメーションを逆再生したい場合はautoreverses
プロパティにtrue
を渡します。
opacityAnimation.autoreverses = true
アニメーションした値を戻さない
アニメーション完了後は自動で値が戻ってしまうので変化したままにしたい場合はisRemovedOnCompletion
プロパティにtrue
を渡し、fillMode
プロパティにCAMediaTimingFillMode.forwards
を渡します。
opacityAnimation.isRemovedOnCompletion = false
opacityAnimation.fillMode = CAMediaTimingFillMode.forwards
アニメーション開始・終了を検知する:CAAnimationDelegate
アニメーションの開始・終了を検知して処理を実行したい場合はCAAnimationDelegate
プロトコルを使用します。animationDidStart
デリゲートメソッドからアニメーションが開始したことをanimationDidStop
からアニメーションが停止したことを検知することができます。
animationDidStop
の引数からはアニメーションの抽象スーパークラスであるCAAnimation
型と完全に終了したことを示すフラグを取得できます。
extension ViewController: CAAnimationDelegate {
func animationDidStart(_ anim: CAAnimation) {
print("アニメーションが開始されたよ")
}
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
if flag {
print("アニメーションが終了したよ")
if let layer = anim.value(forKey: "square.layer") as? CALayer {
layer.backgroundColor = UIColor.gray.cgColor
layer.cornerRadius = 30
layer.removeAnimation(forKey: "opacityAnimation")
}
}
}
}
CAAnimation
型を介して上記のようにアニメーション識別子でアニメーションを以下のsetValue
メソッドで指定した識別子でCALayer
インスタンスを取得することができます。
// 透明度アニメーションの実装
let opacityAnimation = CABasicAnimation(keyPath: "opacity")
opacityAnimation.fromValue = 1
opacityAnimation.toValue = 0
opacityAnimation.duration = 2.0
opacityAnimation.isRemovedOnCompletion = true
opacityAnimation.fillMode = CAMediaTimingFillMode.forwards
// デリゲートをセット
opacityAnimation.delegate = self
// CALayerを渡すように識別子を付与
opacityAnimation.setValue(square.layer, forKey: "square.layer")
square.layer.add(opacityAnimation, forKey: "opacityAnimation")
self.view.addSubview(square)
Viewを無限回転させてみる
独自のローディングビューなどを実装する際に利用できるViewが無限回転するアニメーションを実装するなら以下のおようになります。
let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
// 360度回転
rotationAnimation.toValue = Double.pi * 2.0
rotationAnimation.duration = 3.0
// アニメーションを累積許可
rotationAnimation.isCumulative = true
// アニメーション繰り返し回数に最大の有限数を指定
rotationAnimation.repeatCount = Float.greatestFiniteMagnitude
square.layer.add(rotationAnimation, forKey: "rotationAnimation")
self.view.addSubview(square)
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。