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 анимируются автоматически (animateCABasicAnimation для position, bounds, opacity, backgroundColor). Implicit-анимации используют CATransaction по умолчанию с duration 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). Layer не обрабатывает события касаний — за это отвечает UIView/UIScrollView. Слои образуют иерархию (layer tree), зеркально отражающую view hierarchy.
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 — это temporary-отображение (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 — группировка и completion
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 с duration 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 с duration 0.25 с. Implicit-анимации управляются через CATransaction и отключаются через setDisableActions(true).
Для кастомных свойств реализуйте action(forKey:) в CALayer и верните CAAnimation. Либо используйте CALayer.display() для ручной отрисовки с анимацией через CADisplayLink. Для свойств, поддерживающих CABasicAnimation, достаточно указать keyPath в виде строки.
CALayer легче UIView: не имеет обработчиков событий (touches, gestures), не участвует в Auto Layout и не поддерживает accessibility. Типичный CALayer занимает ~100 байт против ~300+ у UIView. Core Animation обрабатывает слои в render server на GPU без блокировки main thread.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также