Core Animationは、CALayerレベルで動作し、GPUでコンテンツをレンダリングするAppleのローレベルアニメーションフレームワークです。Core AnimationはiOSとmacOSにおけるすべてのアニメーションの基礎です: UIKit、AppKit、SceneKit、SpriteKitはその上に構築されています。この記事では、CALayer、CABasicAnimation、CATransactionのアーキテクチャと実践的な技術について説明します。
メインポイント
Core AnimationはAppleのグラフィックフレームワークで、Mac OS X 10.5 Leopard (2007)で導入され、iPhone OS (2007)の初期バージョンでiOSにポートされました。Core AnimationはGPU上でレイヤー(CALayer)のコンポジション、アニメーション、レンダリングを管理します。iOSの各UIViewには組み込みのCALayer (view.layer)があり、すべての描画はこれを通じて行われます。
Core Animationはインプリシットアニメーションの原理で動作します: いくつかのCALayerプロパティ変更は自動的にアニメートされます(position、bounds、opacity、backgroundColorのCABasicAnimation)。インプリシットアニメーションはデフォルトで0.25秒のdurationとeaseInOutカーブでCATransactionを使用します。
Apple WWDC 2024によると、Core AnimationはProMotionデバイスで最120 FPSまで処理し、GPUレンダリングにMetalを使用します。ティピカルなレイヤーのメモリフットプリントは1ピクセルあたり4バイト(RGBA) + ~100バイトのオーバーヘッドです。安定した60 FPSには、画面上のレイヤーを1000以下にすることが推奨されます。
CALayerはCore Animationの基礎です。各CALayerはビットマップコンテンツ(またはGPUテクスチャ参照)を含み、ジオメトリ(bounds、position、anchorPoint、transform、cornerRadius、borderWidth、shadow)を定義します。レイヤーはタッチイベントを処理しません — UIView/UIScrollViewが処理します。レイヤーはビュー階層を反映する階層構造(layer tree)を形成します。
Core Animationは3つのレイヤーツリーを管理します: model layer tree(実際のプロパティ)、presentation layer tree(アニメーション中の現在の表示)、render tree(GPUデータ)。Presentation layerはリアルタイムの中間アニメーション値を読み取るために使用されます。
CALayerには専門化されたサブクラスがあります: CAShapeLayer(ベクトルグラフィックス)、CAGradientLayer(グラデーション)、CATextLayer(テキスト)、CATiledLayer(タイルに分けられた大きな画像)、CAEAGLLayer/MetalLayer(OpenGL/Metal)。CAShapeLayerはCGPathサポートとstrokeStart/strokeEndアニメーションにより、カスタムアニメーションに最も人気があります。
CABasicAnimationはCAAnimationのサブクラスで、CALayerの1つのプロパティを開始値(fromValue)から終了値(toValue)または相対変更(byValue)によってアニメートします。アニメーションはlayer.add(animation, forKey:)を通じてレイヤーに追加され、各フレームでアプリの参加なしにレンダサーバーで実行されます。
キー(keyPath)はプロパティを指定する文字列です: opacity、position、bounds、transform.rotation.z、cornerRadius、shadowOpacityなど。Core Animationは60以上のアニメート可能なkeyPathをサポートしています。カスタムプロパティには、CATransactionとpresentation layerが使用されます。
import QuartzCore
// opacityとcornerRadiusのCABasicAnimation
func animateLayer() {
let layer = CALayer()
layer.frame = CGRect(x: 50, y: 50, width: 100, height: 100)
layer.backgroundColor = UIColor.systemBlue.cgColor
view.layer.addSublayer(layer)
// 1. 透明度アニメーション
let fadeAnim = CABasicAnimation(keyPath: "opacity")
fadeAnim.fromValue = 1.0
fadeAnim.toValue = 0.2
fadeAnim.duration = 2.0
fadeAnim.autoreverses = true
fadeAnim.repeatCount = .infinity
// 2. 角丸アニメーション
let cornerAnim = CABasicAnimation(keyPath: "cornerRadius")
cornerAnim.fromValue = 0
cornerAnim.toValue = 50
cornerAnim.duration = 2.0
cornerAnim.autoreverses = true
cornerAnim.repeatCount = .infinity
layer.add(fadeAnim, forKey: "fade")
layer.add(cornerAnim, forKey: "corner")
}
// ease-in-outでのpositionアニメーション
func moveLayer(_ layer: CALayer, to point: CGPoint) {
let anim = CABasicAnimation(keyPath: "position")
anim.fromValue = NSValue(cgPoint: layer.position)
anim.toValue = NSValue(cgPoint: point)
anim.duration = 0.6
anim.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
anim.isRemovedOnCompletion = false
anim.fillMode = .forwards
layer.add(anim, forKey: "move")
layer.position = point // model layerを更新
}重要: CABasicAnimationは一時的な表示(presentation layer)です。アニメーションが完了すると、レイヤーはmodel layerの値に戻ります。最終状態を保持するには、isRemovedOnCompletion = false、fillMode = .forwardsを設定し、model layerを最終値に更新します。
CATransactionはCore Animationのアニメーションを単一のトランザクションにグループ化するメカニズムです。CATransaction.begin()とCATransaction.commit()がトランザクションの範囲を定義します。トランザクション内では、共通パラメーター(duration、timingFunction、completionBlock、disableActions)を設定できます。CATransactionはCALayerのインプリシットアニメーションの基礎です。
インプリシットアニメーションは、CALayerプロパティがアニメーションブロックの外で変更された場合に発生します: layer.opacity = 0.5。Core Animationは自動的に現在のCATransaction(デフォルト 0.25 s)からのdurationを持つCABasicAnimationを作成します。インプリシットアニメーションを無効にするには、CATransaction.setDisableActions(true)を使用します。
// CATransaction — グルーピングと完了
func groupedAnimation() {
CATransaction.begin()
CATransaction.setAnimationDuration(1.5)
CATransaction.setAnimationTimingFunction(
CAMediaTimingFunction(name: .easeOut)
)
CATransaction.setCompletionBlock {
print("All animations completed")
}
// トランザクション内のすべての変更はduration 1.5でアニメートされる
layer1.opacity = 0.3
layer2.position = CGPoint(x: 200, y: 300)
layer3.backgroundColor = UIColor.systemRed.cgColor
CATransaction.commit()
}
// インプリシットアニメーションを無効化
func updateWithoutAnimation() {
CATransaction.begin()
CATransaction.setDisableActions(true)
layer.frame = newFrame // アニメーションなし
CATransaction.commit()
}
// CAShapeLayerプロパティをアニメート
func animateProgress(percentage: CGFloat) {
CATransaction.begin()
CATransaction.setAnimationDuration(0.8)
CATransaction.setCompletionBlock {
print("Progress updated to (percentage)%")
}
progressLayer.strokeEnd = percentage / 100.0
CATransaction.commit()
}Core AnimationはUIViewレイヤーを通じてUIKitと連携します。下の例はCAShapeLayerアニメーションを示しています — 弧の長さアニメーションとCABasicAnimationによるレイヤー回転を伴う円形プログレスです。
import UIKit
import QuartzCore
class CircularProgressView: UIView {
private let progressLayer = CAShapeLayer()
override func layoutSubviews() {
super.layoutSubviews()
let path = UIBezierPath(
arcCenter: CGPoint(x: bounds.midX, y: bounds.midY),
radius: bounds.width / 2 - 10,
startAngle: -.pi / 2,
endAngle: 3 * .pi / 2,
clockwise: true
)
progressLayer.path = path.cgPath
progressLayer.strokeColor = UIColor.systemBlue.cgColor
progressLayer.lineWidth = 8
progressLayer.fillColor = nil
progressLayer.strokeEnd = 0
progressLayer.lineCap = .round
layer.addSublayer(progressLayer)
}
func setProgress(_ value: CGFloat, animated: Bool = true) {
if animated {
CATransaction.begin()
CATransaction.setAnimationDuration(0.6)
CATransaction.setAnimationTimingFunction(
CAMediaTimingFunction(name: .easeOut)
)
progressLayer.strokeEnd = min(max(value, 0), 1)
CATransaction.commit()
} else {
progressLayer.strokeEnd = value
}
}
}CAShapeLayerはstrokeEndプロパティを0から1までアニメートし、円形プログレスの“埋め”エフェクトを作り出します。duration 0.6とeaseOutのCATransactionによりアニメーションが滑らかになります。layer.lineCap = .roundは線の端を丸くし、より綺麗な視覚的な仕上がりにします。
よくある質問
Core AnimationはCALayerレベルで動作し、CABasicAnimationとCATransactionによって制御されます。UIView.animateはCore Animationのハイレベルラッパーで、UIViewのプロパティに対するCABasicAnimationを自動的に作成します。Core Animationはより多くの制御を提供します: keyPath、timingFunction、fillMode、CATransactionによるグループ化。
インプリシットアニメーションは、アニメート可能なCALayerプロパティがアニメーションブロックの外で変更された場合に発生する自動アニメーションです。例えば、layer.opacity = 0.5は0.25 sのdurationでCABasicAnimationを作成します。インプリシットアニメーションはCATransactionで管理され、setDisableActions(true)で無効にできます。
カスタムプロパティには、CALayerでaction(forKey:)を実装し、CAAnimationを返します。または、CADisplayLinkを使ったアニメーションで手動描画するためにCALayer.display()を使用します。CABasicAnimationをサポートするプロパティには、keyPathを文字列として指定するだけで協力できます。
CALayerはUIViewより軽いです: イベントハンドラ(touches、gestures)がなく、Auto Layoutに参加せず、アクセシビリティをサポートしません。ティピカルなCALayerは~100バイトですが、UIViewは~300+バイトです。Core Animationはメインスレッドをブロックせずに、GPU上のレンダサーバーでレイヤーを処理します。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。