Core Animation este un framework de animație de nivel scăzut al Apple, care funcționează la nivelul CALayer și asigură randarea conținutului pe GPU. Core Animation stă la baza tuturor animațiilor în iOS și macOS: UIKit, AppKit, SceneKit și SpriteKit sunt construite pe ea. Articolul explică arhitectura CALayer, CABasicAnimation, CATransaction și tehnicile practice.
Principalele puncte
Core Animation este un framework grafic Apple care a apărut în Mac OS X 10.5 Leopard (2007) și a fost portat pe iOS de la prima versiune iPhone OS (2007). Core Animation gestionează compoziția, animația și randarea straturilor (CALayer) pe GPU. Fiecare UIView în iOS are un CALayer încorporat (view.layer) prin care are loc întreaga randare.
Core Animation funcționează pe principiul implicit animation: unele modificări ale proprietăților CALayer sunt animate automat (animează CABasicAnimation pentru position, bounds, opacity, backgroundColor). Implicit-animațiile folosesc CATransaction în mod implicit cu durata de 0.25 secunde și curba easeInOut.
Conform datelor Apple WWDC 2024, Core Animation procesează până la 120 FPS pe dispozitivele ProMotion, folosind Metal pentru randarea GPU. Dimensiunea unui strat tipic în memorie este de 4 octeți pe pixel (RGBA) + overhead ~100 de octeți per strat. Se recomandă cel mult 1000 de straturi pe ecran pentru 60 FPS stabile.
CALayer baza Core Animation. Fiecare CALayer conține conținut bitmap (sau referință la o textură GPU) și definește geometria (bounds, position, anchorPoint, transform, cornerRadius, borderWidth, shadow). Stratul nu procesează evenimente tactile de aceasta se ocupă UIView/UIScrollView. Straturile formează o ierarhie (layer tree) care oglindește ierarhia de vizualizare.
Core Animation suportă trei arbori de straturi: model layer tree (proprietăți reale), presentation layer tree (afișarea curentă în timpul animației) și render tree (date pentru GPU). Presentation layer este folosit pentru citirea valorilor intermediare ale animației în timp real.
CALayer are subclase specializate: CAShapeLayer (grafică vectorială), CAGradientLayer (gradient), CATextLayer (text), CATiledLayer (imagini mari împărțite în segmente), CAEAGLLayer/MetalLayer (OpenGL/Metal). CAShapeLayer este cel mai popular pentru animații personalizate datorită suportului pentru CGPath și animația strokeStart/strokeEnd.
CABasicAnimation este o subclasă CAAnimation care animează o singură proprietate CALayer de la valoarea inițială (fromValue) la valoarea finală (toValue) sau modificarea relativă (byValue). Animația este adăugată stratului prin metoda layer.add(animation, forKey:) și este executată în render server fără participarea aplicației la fiecare cadru.
Cheia (keyPath) este un șir care indică proprietatea: opacity, position, bounds, transform.rotation.z, cornerRadius, shadowOpacity etc. Core Animation suportă 60+ de keyPath-uri animabile. Pentru proprietăți personalizate se folosesc CATransaction și presentation layer.
import QuartzCore
// CABasicAnimation pentru opacity și 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. Animația transparenței
let fadeAnim = CABasicAnimation(keyPath: "opacity")
fadeAnim.fromValue = 1.0
fadeAnim.toValue = 0.2
fadeAnim.duration = 2.0
fadeAnim.autoreverses = true
fadeAnim.repeatCount = .infinity
// 2. Animația rotunjirii colțurilor
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")
}
// Animația position cu 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 // actualizăm model layer
}Important: CABasicAnimation este o afișare temporară (presentation layer). După terminarea animației, stratul revine la valorile inițiale ale model layer. Pentru a fixa starea finală, setați isRemovedOnCompletion = false, fillMode = .forwards și actualizați model layer la valorile finale.
CATransaction este un mecanism de grupare a animațiilor Core Animation într-o singură tranzacție. CATransaction.begin() și CATransaction.commit() definesc granițele tranzacției. În interiorul tranzacției pot fi setați parametri comuni: duration, timingFunction, completionBlock, disableActions. CATransaction stă la baza implicit-animațiilor CALayer.
Implicit animation funcționează atunci când proprietatea CALayer este modificată în afara blocului de animație: layer.opacity = 0.5. Core Animation creează automat CABasicAnimation cu duration din CATransaction curent (implicit 0.25 s). Pentru a dezactiva implicit-animația utilizați CATransaction.setDisableActions(true).
// CATransaction grupare și completare
func groupedAnimation() {
CATransaction.begin()
CATransaction.setAnimationDuration(1.5)
CATransaction.setAnimationTimingFunction(
CAMediaTimingFunction(name: .easeOut)
)
CATransaction.setCompletionBlock {
print("All animations completed")
}
// Toate modificările din tranzacție sunt animate cu durata 1.5
layer1.opacity = 0.3
layer2.position = CGPoint(x: 200, y: 300)
layer3.backgroundColor = UIColor.systemRed.cgColor
CATransaction.commit()
}
// Dezactivarea implicit-animației
func updateWithoutAnimation() {
CATransaction.begin()
CATransaction.setDisableActions(true)
layer.frame = newFrame // fără animație
CATransaction.commit()
}
// Animația proprietăților 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 se integrează cu UIKit prin stratul UIView. Exemplul de mai jos demonstrează animația CAShapeLayer o bară de progres circulară cu animația lungimii arcului și rotația stratului prin 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 animează proprietatea strokeEnd de la 0 la 1, creând efectul de „umplere“ a barei de progres circulare. CATransaction cu durata 0.6 și easeOut face animația lină. layer.lineCap = .round adaugă capete rotunjite ale liniei pentru un aspect mai îngrijit.
Întrebări frecvente
Core Animation funcționează la nivelul CALayer și este gestionată prin CABasicAnimation, CATransaction. UIView.animate este un înveliș de nivel înalt peste Core Animation care creează automat CABasicAnimation pentru proprietățile UIView. Core Animation oferă mai mult control: keyPath, timingFunction, fillMode, gruparea prin CATransaction.
Implicit animation este animația automată la modificarea proprietății animabile CALayer în afara blocului de animație. De exemplu, layer.opacity = 0.5 creează CABasicAnimation cu durata 0.25 s. Implicit-animațiile sunt gestionate prin CATransaction și dezactivate prin setDisableActions(true).
Pentru proprietăți personalizate implementați action(forKey:) în CALayer și returnați CAAnimation. Sau folosiți CALayer.display() pentru randare manuală cu animație prin CADisplayLink. Pentru proprietăți care suportă CABasicAnimation, este suficient să specificați keyPath ca șir de caractere.
CALayer este mai ușor decât UIView: nu are gestionari de evenimente (touches, gesturi), nu participă la Auto Layout și nu suportă accessibility. Un CALayer tipic ocupă ~100 de octeți față de ~300+ pentru UIView. Core Animation procesează straturile în render server pe GPU fără a bloca main thread.
Concluzii
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și