Core Animation — низькорівневий фреймворк анімації Apple, що працює на рівні CALayer та забезпечує відтворення контенту на GPU. Core Animation — основа всіх анімацій в iOS та macOS: UIKit, AppKit, SceneKit та SpriteKit побудовані поверх нього. Стаття пояснює архітектуру CALayer, CABasicAnimation, CATransaction та практичні техніки.
Головне
Core Animation — графічний фреймворк Apple, що з'явився в Mac OS X 10.5 Leopard (2007) і портований на iOS з першою версією iPhone OS (2007). Core Animation керує композицією, анімацією та рендерингом шарів (CALayer) на GPU. Кожен UIView в iOS має вбудований CALayer (view.layer), через який відбувається весь рендеринг.
Core Animation працює за принципом implicit animation: деякі зміни властивостей CALayer анімуються автоматично (CABasicAnimation для position, bounds, opacity, backgroundColor). Неявні анімації використовують CATransaction за замовчуванням з тривалістю 0,25 секунди та кривою easeInOut.
За даними Apple WWDC 2024, Core Animation обробляє до 120 FPS на пристроях з ProMotion, використовуючи Metal для GPU-рендерингу. Розмір типового шару в пам'яті — 4 байти на піксель (RGBA) + накладні витрати ~100 байт на шар. Рекомендується не більше 1000 шарів на екрані для стабільних 60 FPS.
CALayer — основа Core Animation. Кожен CALayer містить bitmap-контент (або посилання на GPU-текстуру) та визначає геометрію (bounds, position, anchorPoint, transform, cornerRadius, borderWidth, shadow). Шари не обробляють події дотиків — за це відповідає UIView/UIScrollView. Шари утворюють ієрархію (layer tree), що дзеркально відображає view hierarchy.
Core Animation підтримує три дерева шарів: 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 від початкового значення (fromValue) до кінцевого (toValue) або відносної зміни (byValue). Анімація додається до шару через метод layer.add(animation, forKey:) та виконується в render server без участі програми на кожному кадрі.
Ключ (keyPath) — рядок, що вказує на властивість: opacity, position, bounds, transform.rotation.z, cornerRadius, shadowOpacity тощо. Core Animation підтримує 60+ анімованих keyPath. Для кастомних властивостей використовується CATransaction та presentation layer.
import QuartzCore
// CABasicAnimation для opacity та cornerRadius
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")
}
// Анімація position з ease-in-out
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 автоматично створює CABasicAnimation з duration з поточного CATransaction (за замовчуванням 0,25 с). Для вимкнення неявної анімації використовуйте 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 інтегрується з UIKit через шар UIView. Приклад нижче демонструє анімацію 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, створюючи ефект «заповнення» кругового прогресу. CATransaction з duration 0.6 та easeOut робить анімацію плавною. layer.lineCap = .round додає округлені кінці лінії для більш акуратного візуалу.
Часті запитання
Core Animation працює на рівні CALayer та керується через CABasicAnimation, CATransaction. UIView.animate — високорівнева обгортка над Core Animation, що автоматично створює CABasicAnimation для властивостей UIView. Core Animation дає більше контролю: keyPath, timingFunction, fillMode, групування через CATransaction.
Implicit animation — автоматична анімація при зміні анімованої властивості CALayer поза блоком анімації. Наприклад, layer.opacity = 0.5 створює CABasicAnimation з duration 0,25 с. Неявні анімації керуються через CATransaction та вимикаються через setDisableActions(true).
Для кастомних властивостей реалізуйте action(forKey:) в CALayer та поверніть CAAnimation. Або використовуйте CALayer.display() для ручного рендерингу з анімацією через CADisplayLink. Для властивостей, що підтримують CABasicAnimation, достатньо вказати keyPath у вигляді рядка.
CALayer легший за UIView: не має обробників подій (touches, gestures), не бере участі в Auto Layout та не підтримує accessibility. Типовий CALayer займає ~100 байт проти ~300+ у UIView. Core Animation обробляє шари в render server на GPU без блокування головного потоку.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також