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 byte ανά pixel (RGBA) + επιβάρυνση ~100 byte ανά επίπεδο. Συνιστώνται όχι περισσότερα από 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 byte έναντι ~300+ για UIView. Το Core Animation επεξεργάζεται τα επίπεδα στον render server στην GPU χωρίς να μπλοκάρει το main thread.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης