Core Animation یک فریمورک انیمیشن سطح پایین اپل است که در سطح CALayer کار میکند و رندر کردن محتوا را روی GPU تضمین میکند. Core Animation اساس تمام انیمیشنهای iOS و macOS است: UIKit، AppKit، SceneKit و SpriteKit بر روی آن ساخته شدهاند. این مقاله معماری CALayer، CABasicAnimation، CATransaction و تکنیکهای عملی را توضیح میدهد.
نکات کلیدی
Core Animation یک فریمورک گرافیکی اپل است که در Mac OS X 10.5 Leopard (2007) ظاهر شد و از اولین نسخه iPhone OS (2007) به iOS منتقل شد. 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 بایت بر پیکسل (RGBA) + سربار ~100 بایت بر لایه است. برای 60 FPS پایدار بیش از 1000 لایه روی صفحه توصیه نمیشود.
CALayer اساس Core Animation است. هر CALayer حاوی محتوای بیتمپ (یا ارجاع به بافت GPU) است و هندسه (bounds، position، anchorPoint، transform، cornerRadius، borderWidth، shadow) را تعریف میکند. لایه رویدادهای لمسی را پردازش نمیکند UIView/UIScrollView مسئول این کار است. لایهها سلسلهمراتبی (layer tree) تشکیل میدهند که نمایانگر سلسلهمراتب view است.
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 گروهبندی و completion
func groupedAnimation() {
CATransaction.begin()
CATransaction.setAnimationDuration(1.5)
CATransaction.setAnimationTimingFunction(
CAMediaTimingFunction(name: .easeOut)
)
CATransaction.setCompletionBlock {
print("All animations completed")
}
// تمام تغییرات داخل تراکنش با مدت 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 از طریق لایه UIView با UIKit ادغام میشود. مثال زیر انیمیشن 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، gestures) ندارد، در Auto Layout شرکت نمیکند و از accessibility پشتیبانی نمیکند. یک CALayer معمولی ~100 بایت در مقابل ~300+ برای UIView اشغال میکند. Core Animation لایهها را در render server روی GPU بدون مسدود کردن main thread پردازش میکند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید