Core Animation – niskopoziomowy framework animacji Apple, działający na poziomie CALayer i zapewniający renderowanie treści na GPU. Core Animation jest podstawą wszystkich animacji w iOS i macOS: UIKit, AppKit, SceneKit i SpriteKit są zbudowane na jej bazie. Artykuł wyjaśnia architekturę CALayer, CABasicAnimation, CATransaction oraz praktyczne techniki.
Najważniejsze
Core Animation – graficzny framework Apple, który pojawił się w Mac OS X 10.5 Leopard (2007) i został przeniesiony na iOS od pierwszej wersji iPhone OS (2007). Core Animation zarządza kompozycją, animacją i renderowaniem warstw (CALayer) na GPU. Każdy UIView w iOS ma wbudowaną warstwę CALayer (view.layer), przez którą odbywa się całe renderowanie.
Core Animation działa na zasadzie implicit animation: niektóre zmiany właściwości CALayer są animowane automatycznie (animują CABasicAnimation dla position, bounds, opacity, backgroundColor). Implicit-animacje używają CATransaction domyślnie z czasem trwania 0,25 sekundy i krzywą easeInOut.
Według danych Apple WWDC 2024, Core Animation przetwarza do 120 FPS na urządzeniach z ProMotion, wykorzystując Metal do renderowania na GPU. Rozmiar typowej warstwy w pamięci to 4 bajty na piksel (RGBA) + narzut ~100 bajtów na warstwę. Zaleca się nie więcej niż 1000 warstw na ekran dla stabilnych 60 FPS.
CALayer – podstawa Core Animation. Każdy CALayer zawiera treść bitmapową (lub odnośnik do tekstury GPU) i określa geometrię (bounds, position, anchorPoint, transform, cornerRadius, borderWidth, shadow). Warstwa nie obsługuje zdarzeń dotyku – odpowiada za to UIView/UIScrollView. Warstwy tworzą hierarchię (layer tree), odzwierciedlającą hierarchię widoków.
Core Animation obsługuje trzy drzewa warstw: model layer tree (rzeczywiste właściwości), presentation layer tree (bieżący stan podczas animacji) i render tree (dane dla GPU). Presentation layer jest używany do odczytywania pośrednich wartości animacji w czasie rzeczywistym.
CALayer ma wyspecjalizowane podklasy: CAShapeLayer (grafika wektorowa), CAGradientLayer (gradient), CATextLayer (tekst), CATiledLayer (duże obrazy podzielone na kafelki), CAEAGLLayer/MetalLayer (OpenGL/Metal). CAShapeLayer jest najpopularniejszy do niestandardowych animacji dzięki obsłudze CGPath i animacji strokeStart/strokeEnd.
CABasicAnimation – podklasa CAAnimation, animująca jedną właściwość CALayer od wartości początkowej (fromValue) do końcowej (toValue) lub względnej zmiany (byValue). Animacja jest dodawana do warstwy przez metodę layer.add(animation, forKey:) i wykonywana w render server bez udziału aplikacji na każdej klatce.
Klucz (keyPath) – ciąg znaków wskazujący właściwość: opacity, position, bounds, transform.rotation.z, cornerRadius, shadowOpacity itd. Core Animation obsługuje 60+ animowanych keyPath. Dla niestandardowych właściwości używa się CATransaction i presentation layer.
import QuartzCore
// CABasicAnimation dla 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. Animacja przezroczystości
let fadeAnim = CABasicAnimation(keyPath: "opacity")
fadeAnim.fromValue = 1.0
fadeAnim.toValue = 0.2
fadeAnim.duration = 2.0
fadeAnim.autoreverses = true
fadeAnim.repeatCount = .infinity
// 2. Animacja zaokrąglenia rogów
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")
}
// Animacja position z 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 // aktualizujemy model layer
}Ważne: CABasicAnimation to tymczasowe wyświetlanie (presentation layer). Po zakończeniu animacji warstwa wraca do początkowych wartości model layer. Aby zatrzymać stan końcowy, ustaw isRemovedOnCompletion = false, fillMode = .forwards i zaktualizuj model layer do wartości końcowych.
CATransaction – mechanizm grupowania animacji Core Animation w jedną transakcję. CATransaction.begin() i CATransaction.commit() określają granice transakcji. Wewnątrz transakcji można ustawić wspólne parametry: duration, timingFunction, completionBlock, disableActions. CATransaction jest podstawą implicit-animacji CALayer.
Implicit animation działa, gdy właściwość CALayer jest zmieniana poza blokiem animacji: layer.opacity = 0.5. Core Animation automatycznie tworzy CABasicAnimation z czasem trwania z bieżącego CATransaction (domyślnie 0,25 s). Aby wyłączyć implicit-animacje, użyj CATransaction.setDisableActions(true).
// CATransaction – grupowanie i completion
func groupedAnimation() {
CATransaction.begin()
CATransaction.setAnimationDuration(1.5)
CATransaction.setAnimationTimingFunction(
CAMediaTimingFunction(name: .easeOut)
)
CATransaction.setCompletionBlock {
print("All animations completed")
}
// Wszystkie zmiany wewnątrz transakcji są animowane z czasem trwania 1.5
layer1.opacity = 0.3
layer2.position = CGPoint(x: 200, y: 300)
layer3.backgroundColor = UIColor.systemRed.cgColor
CATransaction.commit()
}
// Wyłączenie implicit-animacji
func updateWithoutAnimation() {
CATransaction.begin()
CATransaction.setDisableActions(true)
layer.frame = newFrame // bez animacji
CATransaction.commit()
}
// Animacja właściwości 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 integruje się z UIKit przez warstwę UIView. Poniższy przykład demonstruje animację CAShapeLayer – okrągły pasek postępu z animacją długości łuku i obrotem warstwy przez 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 animuje właściwość strokeEnd od 0 do 1, tworząc efekt „wypełniania” okrągłego paska postępu. CATransaction z czasem trwania 0,6 i easeOut sprawia, że animacja jest płynna. layer.lineCap = .round dodaje zaokrąglone końce linii dla bardziej estetycznego wyglądu.
Często zadawane pytania
Core Animation działa na poziomie CALayer i jest zarządzana przez CABasicAnimation, CATransaction. UIView.animate to wysokopoziomowe opakowanie nad Core Animation, automatycznie tworzące CABasicAnimation dla właściwości UIView. Core Animation daje więcej kontroli: keyPath, timingFunction, fillMode, grupowanie przez CATransaction.
Implicit animation to automatyczna animacja przy zmianie animowanej właściwości CALayer poza blokiem animacji. Na przykład layer.opacity = 0.5 tworzy CABasicAnimation z czasem trwania 0,25 s. Implicit-animacje są zarządzane przez CATransaction i wyłączane przez setDisableActions(true).
Dla niestandardowych właściwości zaimplementuj action(forKey:) w CALayer i zwróć CAAnimation. Możesz też użyć CALayer.display() do ręcznego renderowania z animacją przez CADisplayLink. Dla właściwości obsługujących CABasicAnimation wystarczy podać keyPath jako ciąg znaków.
CALayer jest lżejszy od UIView: nie ma obsługi zdarzeń (touches, gesty), nie uczestniczy w Auto Layout i nie wspiera accessibility. Typowy CALayer zajmuje ~100 bajtów wobec ~300+ dla UIView. Core Animation przetwarza warstwy w render server na GPU bez blokowania main thread.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również