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). Implicit-анимациите използват CATransaction по подразбиране с продължителност 0.25 секунди и крива easeInOut.
Според данни на Apple WWDC 2024, Core Animation обработва до 120 FPS на устройства с ProMotion, използвайки Metal за GPU визуализация. Размерът на типичен слой в паметта е 4 байта на пиксел (RGBA) + overhead ~100 байта на слой. Препоръчват се не повече от 1000 слоя на екран за стабилни 60 FPS.
CALayer основата на Core Animation. Всеки CALayer съдържа bitmap съдържание (или препратка към GPU текстура) и дефинира геометрия (bounds, position, anchorPoint, transform, cornerRadius, borderWidth, shadow). Слоят не обработва събития на допир за това отговаря UIView/UIScrollView. Слоевете образуват йерархия (layer tree), отразяваща йерархията на изгледите.
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 е основата на implicit-анимациите на CALayer.
Implicit animation работи, когато свойство на CALayer се променя извън анимационния блок: layer.opacity = 0.5. Core Animation автоматично създава CABasicAnimation с duration от текущия CATransaction (по подразбиране 0.25 сек). За изключване на implicit-анимация използвайте 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()
}
// Изключване на implicit-анимация
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 с продължителност 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 с продължителност 0.25 сек. Implicit-анимациите се управляват чрез CATransaction и се изключват чрез setDisableActions(true).
За персонализирани свойства имплементирайте action(forKey:) в CALayer и върнете CAAnimation. Или използвайте CALayer.display() за ръчна визуализация с анимация чрез CADisplayLink. За свойства, поддържащи CABasicAnimation, достатъчно е да посочите keyPath като низ.
CALayer е по-лек от UIView: няма обработчици на събития (touches, жестове), не участва в Auto Layout и не поддържа accessibility. Типичен CALayer заема ~100 байта срещу ~300+ за UIView. Core Animation обработва слоевете в render server на GPU без блокиране на main thread.
Резюме
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също