Core Animation là framework hoạt hình cấp thấp của Apple hoạt động ở cấp độ CALayer và kết xuất nội dung trên GPU. Core Animation là nền tảng của tất cả các hoạt hình trong iOS và macOS: UIKit, AppKit, SceneKit và SpriteKit được xây dựng trên nó. Bài viết này giải thích kiến trúc của CALayer, CABasicAnimation, CATransaction và các kỹ thuật thực tế.
Các điểm chính
Core Animation là framework đồ họa của Apple được giới thiệu trong Mac OS X 10.5 Leopard (2007) và được chuyển sang iOS với phiên bản đầu tiên của iPhone OS (2007). Core Animation quản lý việc tổng hợp, hoạt hình và kết xuất các lớp (CALayer) trên GPU. Mỗi UIView trong iOS có một CALayer tích hợp sẵn (view.layer) thông qua đó tất cả việc vẽ diễn ra.
Core Animation hoạt động theo nguyên tắc hoạt hình ngầm: một số thay đổi thuộc tính CALayer tự động hoạt hình (CABasicAnimation cho position, bounds, opacity, backgroundColor). Các hoạt hình ngầm sử dụng CATransaction theo mặc định với thời lượng 0,25 giây và đường cong easeInOut.
Theo Apple WWDC 2024, Core Animation xử lý đến 120 FPS trên các thiết bị ProMotion, sử dụng Metal để kết xuất GPU. Dung lượng bộ nhớ của một lớp điển hình là 4 byte trên mỗi pixel (RGBA) + ~100 byte chi phí trên mỗi lớp. Nên giữ không quá 1000 lớp trên màn hình để đạt 60 FPS ổn định.
CALayer là nền tảng của Core Animation. Mỗi CALayer chứa nội dung bitmap (hoặc tham chiếu kết cấu GPU) và xác định hình học (bounds, position, anchorPoint, transform, cornerRadius, borderWidth, shadow). Các lớp không xử lý các sự kiện chạm — UIView/UIScrollView xử lý việc đó. Các lớp tạo thành một hệ phân cấp (layer tree) phản ánh hệ phân cấp view.
Core Animation duy trì ba cây lớp: model layer tree (các thuộc tính thực tế), presentation layer tree (hiển thị hiện tại trong quá trình hoạt hình) và render tree (dữ liệu GPU). Lớp trình bày được sử dụng để đọc các giá trị hoạt hình trung gian theo thời gian thực.
CALayer có các lớp con chuyên biệt: CAShapeLayer (đồ họa vector), CAGradientLayer (chuyển sắc), CATextLayer (văn bản), CATiledLayer (hình ảnh lớn được chia thành các ô), CAEAGLLayer/MetalLayer (OpenGL/Metal). CAShapeLayer phổ biến nhất cho hoạt hình tùy chỉnh nhờ hỗ trợ CGPath và hoạt hình strokeStart/strokeEnd.
CABasicAnimation là một lớp con của CAAnimation, hoạt hình một thuộc tính CALayer duy nhất từ giá trị bắt đầu (fromValue) đến giá trị kết thúc (toValue) hoặc bằng một thay đổi tương đối (byValue). Hoạt hình được thêm vào lớp thông qua layer.add(animation, forKey:) và chạy trong máy chủ kết xuất mà không cần sự tham gia của ứng dụng trên mỗi khung hình.
Khóa (keyPath) là một chuỗi chỉ định thuộc tính: opacity, position, bounds, transform.rotation.z, cornerRadius, shadowOpacity, v.v. Core Animation hỗ trợ hơn 60 keyPath có thể hoạt hình. Đối với các thuộc tính tùy chỉnh, CATransaction và lớp trình bày được sử dụng.
import QuartzCore
// CABasicAnimation cho opacity và 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. Hoạt hình độ trong suốt
let fadeAnim = CABasicAnimation(keyPath: "opacity")
fadeAnim.fromValue = 1.0
fadeAnim.toValue = 0.2
fadeAnim.duration = 2.0
fadeAnim.autoreverses = true
fadeAnim.repeatCount = .infinity
// 2. Hoạt hình bán kính góc
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")
}
// Hoạt hình position với 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 // cập nhật model layer
}Quan trọng: CABasicAnimation là một hiển thị tạm thời (presentation layer). Sau khi hoạt hình hoàn tất, lớp trở về các giá trị model layer. Để cố định trạng thái cuối, đặt isRemovedOnCompletion = false, fillMode = .forwards và cập nhật model layer thành các giá trị cuối.
CATransaction là một cơ chế nhóm các hoạt hình Core Animation thành một giao dịch duy nhất. CATransaction.begin() và CATransaction.commit() xác định ranh giới giao dịch. Bên trong một giao dịch, có thể đặt các tham số chung: duration, timingFunction, completionBlock, disableActions. CATransaction là nền tảng của các hoạt hình ngầm CALayer.
Hoạt hình ngầm xảy ra khi một thuộc tính CALayer thay đổi bên ngoài khối hoạt hình: layer.opacity = 0.5. Core Animation tự động tạo một CABasicAnimation với thời lượng từ CATransaction hiện tại (mặc định 0.25 s). Để tắt hoạt hình ngầm, sử dụng CATransaction.setDisableActions(true).
// CATransaction — nhóm và hoàn thành
func groupedAnimation() {
CATransaction.begin()
CATransaction.setAnimationDuration(1.5)
CATransaction.setAnimationTimingFunction(
CAMediaTimingFunction(name: .easeOut)
)
CATransaction.setCompletionBlock {
print("All animations completed")
}
// Tất cả các thay đổi trong giao dịch được hoạt hình với thời lượng 1.5
layer1.opacity = 0.3
layer2.position = CGPoint(x: 200, y: 300)
layer3.backgroundColor = UIColor.systemRed.cgColor
CATransaction.commit()
}
// Tắt hoạt hình ngầm
func updateWithoutAnimation() {
CATransaction.begin()
CATransaction.setDisableActions(true)
layer.frame = newFrame // không hoạt hình
CATransaction.commit()
}
// Hoạt hình các thuộc tính 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 tích hợp với UIKit thông qua lớp UIView. Ví dụ dưới đây minh họa hoạt hình CAShapeLayer — một tiến trình hình tròn với hoạt hình độ dài cung và xoay lớp thông qua 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 hoạt hình thuộc tính strokeEnd từ 0 đến 1, tạo hiệu ứng “đổ đầy” tiến trình hình tròn. CATransaction với thời lượng 0.6 và easeOut làm cho hoạt hình mượt mà. layer.lineCap = .round thêm các đầu tròn cho đường kẻ để có giao diện sạch hơn.
Câu hỏi thường gặp
Core Animation hoạt động ở cấp độ CALayer và được điều khiển qua CABasicAnimation và CATransaction. UIView.animate là một wrapper cấp cao bao quanh Core Animation, tự động tạo CABasicAnimation cho các thuộc tính UIView. Core Animation cung cấp nhiều quyền kiểm soát hơn: keyPath, timingFunction, fillMode, nhóm qua CATransaction.
Hoạt hình ngầm là hoạt hình tự động xảy ra khi một thuộc tính có thể hoạt hình của CALayer được thay đổi bên ngoài khối hoạt hình. Ví dụ, layer.opacity = 0.5 tạo CABasicAnimation với thời lượng 0.25 s. Các hoạt hình ngầm được quản lý qua CATransaction và có thể bị tắt bằng setDisableActions(true).
Đối với các thuộc tính tùy chỉnh, hãy triển khai action(forKey:) trong CALayer và trả về một CAAnimation. Hoặc sử dụng CALayer.display() để vẽ thủ công với hoạt hình qua CADisplayLink. Đối với các thuộc tính hỗ trợ CABasicAnimation, chỉ cần chỉ định keyPath dưới dạng chuỗi.
CALayer nhẹ hơn UIView: không có trình xử lý sự kiện (touches, gestures), không tham gia Auto Layout và không hỗ trợ khả năng tiếp cận. Một CALayer điển hình chiếm ~100 byte so với ~300+ của UIView. Core Animation xử lý các lớp trong máy chủ kết xuất trên GPU mà không chặn luồng chính.
Tóm tắt
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm