Core Animation is Apple’s low-level animation framework that operates at the CALayer level and renders content on the GPU. Core Animation is the foundation of all animations in iOS and macOS: UIKit, AppKit, SceneKit, and SpriteKit are built on top of it. This article explains the architecture of CALayer, CABasicAnimation, CATransaction, and practical techniques.
Key Takeaways
Core Animation is Apple’s graphics framework introduced in Mac OS X 10.5 Leopard (2007) and ported to iOS with the first version of iPhone OS (2007). Core Animation manages composition, animation, and rendering of layers (CALayer) on the GPU. Every UIView in iOS has a built-in CALayer (view.layer) through which all drawing occurs.
Core Animation operates on the principle of implicit animation: some CALayer property changes animate automatically (CABasicAnimation for position, bounds, opacity, backgroundColor). Implicit animations use CATransaction by default with a duration of 0.25 seconds and an easeInOut curve.
According to Apple WWDC 2024, Core Animation processes up to 120 FPS on ProMotion devices, using Metal for GPU rendering. A typical layer’s memory footprint is 4 bytes per pixel (RGBA) + ~100 bytes overhead per layer. It is recommended to have no more than 1000 layers on screen for stable 60 FPS.
CALayer is the foundation of Core Animation. Each CALayer contains bitmap content (or a GPU texture reference) and defines geometry (bounds, position, anchorPoint, transform, cornerRadius, borderWidth, shadow). Layers do not handle touch events — UIView/UIScrollView handle that. Layers form a hierarchy (layer tree) that mirrors the view hierarchy.
Core Animation maintains three layer trees: the model layer tree (actual properties), the presentation layer tree (current display during animation), and the render tree (GPU data). The presentation layer is used to read real-time intermediate animation values.
CALayer has specialized subclasses: CAShapeLayer (vector graphics), CAGradientLayer (gradient), CATextLayer (text), CATiledLayer (large images split into tiles), CAEAGLLayer/MetalLayer (OpenGL/Metal). CAShapeLayer is the most popular for custom animation thanks to CGPath support and strokeStart/strokeEnd animation.
CABasicAnimation is a subclass of CAAnimation that animates a single CALayer property from a starting value (fromValue) to an ending value (toValue) or by a relative change (byValue). The animation is added to the layer via layer.add(animation, forKey:) and runs in the render server without app involvement on each frame.
The key (keyPath) is a string specifying the property: opacity, position, bounds, transform.rotation.z, cornerRadius, shadowOpacity, etc. Core Animation supports 60+ animatable keyPaths. For custom properties, CATransaction and the presentation layer are used.
import QuartzCore
// CABasicAnimation for opacity and 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. Opacity animation
let fadeAnim = CABasicAnimation(keyPath: "opacity")
fadeAnim.fromValue = 1.0
fadeAnim.toValue = 0.2
fadeAnim.duration = 2.0
fadeAnim.autoreverses = true
fadeAnim.repeatCount = .infinity
// 2. Corner radius animation
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 animation with 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 // updating model layer
}Important: CABasicAnimation is a temporary display (presentation layer). After the animation completes, the layer returns to its model layer values. To freeze the final state, set isRemovedOnCompletion = false, fillMode = .forwards, and update the model layer to the final values.
CATransaction is a mechanism for grouping Core Animation animations into a single transaction. CATransaction.begin() and CATransaction.commit() define the transaction boundaries. Inside a transaction, shared parameters can be set: duration, timingFunction, completionBlock, disableActions. CATransaction is the foundation of CALayer implicit animations.
Implicit animation occurs when a CALayer property changes outside an animation block: layer.opacity = 0.5. Core Animation automatically creates a CABasicAnimation with the duration from the current CATransaction (default 0.25 s). To disable implicit animation, use CATransaction.setDisableActions(true).
// CATransaction — grouping and completion
func groupedAnimation() {
CATransaction.begin()
CATransaction.setAnimationDuration(1.5)
CATransaction.setAnimationTimingFunction(
CAMediaTimingFunction(name: .easeOut)
)
CATransaction.setCompletionBlock {
print("All animations completed")
}
// All changes within the transaction are animated with duration 1.5
layer1.opacity = 0.3
layer2.position = CGPoint(x: 200, y: 300)
layer3.backgroundColor = UIColor.systemRed.cgColor
CATransaction.commit()
}
// Disabling implicit animation
func updateWithoutAnimation() {
CATransaction.begin()
CATransaction.setDisableActions(true)
layer.frame = newFrame // without animation
CATransaction.commit()
}
// Animating CAShapeLayer properties
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 integrates with UIKit through the UIView layer. The example below demonstrates CAShapeLayer animation — a circular progress with arc length animation and layer rotation via 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 animates the strokeEnd property from 0 to 1, creating a circular progress “fill” effect. CATransaction with duration 0.6 and easeOut makes the animation smooth. layer.lineCap = .round adds rounded line ends for a cleaner visual.
Frequently Asked Questions
Core Animation works at the CALayer level and is controlled via CABasicAnimation and CATransaction. UIView.animate is a high-level wrapper around Core Animation that automatically creates CABasicAnimation for UIView properties. Core Animation provides more control: keyPath, timingFunction, fillMode, grouping via CATransaction.
Implicit animation is automatic animation that occurs when an animatable CALayer property is changed outside an animation block. For example, layer.opacity = 0.5 creates a CABasicAnimation with 0.25 s duration. Implicit animations are managed via CATransaction and can be disabled using setDisableActions(true).
For custom properties, implement action(forKey:) in CALayer and return a CAAnimation. Alternatively, use CALayer.display() for manual drawing with animation via CADisplayLink. For properties that support CABasicAnimation, simply specify the keyPath as a string.
CALayer is lighter than UIView: it has no event handlers (touches, gestures), does not participate in Auto Layout, and does not support accessibility. A typical CALayer takes ~100 bytes compared to ~300+ for UIView. Core Animation processes layers in the render server on the GPU without blocking the main thread.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also