CAShapeLayer — це підклас CALayer, призначений для апаратного відтворення векторних контурів на GPU. На відміну від базового CALayer, який працює з растровими зображеннями через contents, CAShapeLayer рендерить довільні фігури, задані через CGPath, без створення окремого offscreen-буфера. За даними Apple QuartzCore Documentation, 2025, CAShapeLayer використовує апаратне прискорення для згладжування країв і підтримує анімацію path, strokeStart та strokeEnd. Це стандартний інструмент для створення іконок, індикаторів прогресу та масок в iOS.
Головне
CAShapeLayer — це спеціалізований шар для рендерингу векторної графіки, представлений в iOS 3.0. На відміну від CALayer, який зберігає растрове зображення, CAShapeLayer зберігає математичний опис контуру — CGPath — і рендерить його безпосередньо на GPU. Це дозволяє масштабувати фігуру без втрати якості та анімувати її властивості без перемальовування вмісту.
Архітектурно CAShapeLayer успадковує всі можливості CALayer: тіні, межі, трансформації, маски. Але додає специфічні властивості для роботи з контурами: path, fillColor, strokeColor, lineWidth, lineCap, lineJoin, miterLimit, strokeStart, strokeEnd та fillRule. Кожна з цих властивостей може бути анімована через CABasicAnimation.
За даними Apple Documentation, CAShapeLayer використовує апаратне прискорення при рендерингу. На відміну від малювання через Core Graphics в drawRect:, де CPU відтворює контур у растровий контекст, CAShapeLayer надсилає команди безпосередньо на GPU через Metal. Результат — вища продуктивність, особливо під час анімації.
CAShapeLayer особливо корисний для елементів інтерфейсу, які змінюють форму у відповідь на дії користувача: індикатори прогресу, графіки, іконки таб-бару, маски для анімації появи контенту.
Процес рендерингу CAShapeLayer відрізняється від базового CALayer. Замість зберігання растрового буфера з пікселями, CAShapeLayer передає GPU вершинний опис контуру — набір точок, кривих Безьє та відрізків. GPU растеризує цей контур за один прохід, застосовуючи колір заливки та обведення на етапі фрагментного шейдера.
Цей підхід дає дві переваги: масштабування без втрати якості (векторна графіка залишається чіткою при будь-якому розширенні) та ефективна анімація — при зміні path GPU перераховує лише змінені пікселі, а не весь екран.
path — центральна властивість CAShapeLayer, що приймає CGPath. Контур може містити лінії, дуги, криві Безьє та прямокутники. Створюється через UIBezierPath в Swift або CGPath в Core Graphics. Приклад: UIBezierPath(roundedRect: rect, cornerRadius: 16).cgPath створює закруглений прямокутник.
Властивість lineDashPattern дозволяє створювати пунктирні лінії. Приймає масив чисел: [6, 3] означає 6 точок штриха та 3 точки пробілу. lineDashPhase задає зміщення патерну та може анімуватися для ефекту рухомого пунктиру.
Вибір між CAShapeLayer та базовим CALayer залежить від типу контенту. Якщо елемент — прямокутна область з фоном, межею та тінню — достатньо CALayer. Якщо елемент має довільну форму, потребує анімації контуру або масштабування без втрати якості — CAShapeLayer кращий.
Продуктивність також відрізняється. CALayer з cornerRadius та masksToBounds створює offscreen-буфер для обрізки вмісту, що додає 2–5 мс на кадр. CAShapeLayer не потребує offscreen-буфера — обрізка виконується на рівні вершинного шейдера. Для елементів зі складною маскою CAShapeLayer може бути в 2–3 рази швидшим.
Комбінування CAShapeLayer з іншими шарами — поширений патерн. Наприклад, CAGradientLayer використовує CAShapeLayer як маску для створення фігури з градієнтною заливкою. Такий підхід дає і векторну якість, і градієнтне заповнення без втрати продуктивності.
Анімація path — одна з найпотужніших можливостей CAShapeLayer. Якщо обидва контури (початковий та кінцевий) мають однакову кількість контрольних точок та сегментів, Core Animation виконує плавну інтерполяцію між ними. Результат — ефект морфінгу однієї фігури в іншу.
Умова сумісності path: початковий та кінцевий UIBezierPath повинні мати однакову структуру — кількість викликів moveTo, addLine, addCurve та addQuadCurve має збігатися. Якщо структура відрізняється, анімація буде миттєвою, без інтерполяції.
За даними Apple WWDC 2024, комбінація strokeStart та strokeEnd — найбільш часто використовуваний патерн анімації CAShapeLayer. Анімація strokeEnd від 0.0 до 1.0 створює ефект малювання контуру від початку до кінця. Це використовується в індикаторах завантаження, анімації появи графіків та кастомних переходах між екранами.
Для створення пульсуючого ефекту анімуйте одночасно strokeEnd та opacity з різними timing functions. Для ефекту біжучого пунктиру анімуйте lineDashPhase при фіксованому lineDashPattern. Обидва прийоми реалізуються через CABasicAnimation без участі CPU.
CAShapeLayer часто використовується як маска для інших шарів. Наприклад, для анімації появи контенту: на початковому етапі mask.path — це прямокутник нульового розміру, кінцевий — повний розмір. Core Animation інтерполює path, і контент відкривається плавно.
Цей патерн використовується в iOS для анімації розгортання карток, появи модальних вікон та transition між view controllers. На відміну від анімації alpha, анімація через маску CAShapeLayer дає більш природний ефект появи з довільною формою.
Перший приклад — створення круглого індикатора прогресу з анімацією strokeEnd. Це стандартний UI-елемент, який демонструє основні можливості CAShapeLayer:
func createProgressIndicator(in view: UIView) -> CAShapeLayer {
let circlePath = UIBezierPath(
arcCenter: CGPoint(x: 50, y: 50),
radius: 40,
startAngle: -.pi / 2,
endAngle: 3 * .pi / 2,
clockwise: true
)
let trackLayer = CAShapeLayer()
trackLayer.path = circlePath.cgPath
trackLayer.strokeColor = UIColor.systemGray5.cgColor
trackLayer.fillColor = nil
trackLayer.lineWidth = 8
view.layer.addSublayer(trackLayer)
let progressLayer = CAShapeLayer()
progressLayer.path = circlePath.cgPath
progressLayer.strokeColor = UIColor.systemBlue.cgColor
progressLayer.fillColor = nil
progressLayer.lineWidth = 8
progressLayer.lineCap = .round
progressLayer.strokeEnd = 0
view.layer.addSublayer(progressLayer)
return progressLayer
}
// Анімувати до 75%
progressLayer.animateStrokeEnd(to: 0.75, duration: 1.5)
Другий приклад — анімація морфінгу між кругом та квадратом. Обидва UIBezierPath мають сумісну структуру:
let morphLayer = CAShapeLayer()
morphLayer.fillColor = UIColor.systemPurple.cgColor
morphLayer.frame = rect
let circlePath = UIBezierPath(roundedRect: rect, cornerRadius: rect.width / 2)
let squarePath = UIBezierPath(roundedRect: rect, cornerRadius: 0)
morphLayer.path = circlePath.cgPath
let morphAnimation = CABasicAnimation(keyPath: "path")
morphAnimation.toValue = squarePath.cgPath
morphAnimation.duration = 0.6
morphAnimation.autoreverses = true
morphAnimation.repeatCount = .infinity
morphLayer.add(morphAnimation, forKey: "morph")
Третій приклад — маска з CAShapeLayer для анімації появи контенту. Маска розширюється від центру до країв:
func revealContent(_ view: UIView) {
let maskLayer = CAShapeLayer()
let size = view.bounds.size.width * 1.5
let center = CGPoint(x: view.bounds.midX, y: view.bounds.midY)
let initialPath = UIBezierPath(arcCenter: center, radius: 0, startAngle: 0, endAngle: .pi * 2, clockwise: true)
let finalPath = UIBezierPath(arcCenter: center, radius: size, startAngle: 0, endAngle: .pi * 2, clockwise: true)
maskLayer.path = initialPath.cgPath
view.layer.mask = maskLayer
let animation = CABasicAnimation(keyPath: "path")
animation.toValue = finalPath.cgPath
animation.duration = 0.4
animation.timingFunction = CAMediaTimingFunction(name: .easeOut)
maskLayer.add(animation, forKey: "reveal")
maskLayer.path = finalPath.cgPath
}
Поширені запитання
drawRect використовує CPU для відтворення в растровий контекст — це повільно для анімацій. CAShapeLayer рендерить контур на GPU, що дає 60 FPS при анімації path, strokeEnd та трансформацій. Крім того, CAShapeLayer не потребує перемальовування при зміні розмірів — фігура масштабується векторно.
Плавна анімація path потребує однакової структури початкового та кінцевого UIBezierPath. Кількість викликів moveTo, addLine, addCurve має збігатися. Використовуйте UIBezierPath з однаковою кількістю сегментів для початкової та кінцевої фігури.
Задайте lineDashPattern — масив чисел, що чергують довжину штриха та пробілу. Наприклад, [8, 4] створює штрихи 8pt з пробілами 4pt. Для біжучого пунктиру анімуйте lineDashPhase через CABasicAnimation з лінійною timing function.
Так, це один з основних сценаріїв. Призначте CAShapeLayer властивості mask будь-якого CALayer, включаючи CAGradientLayer та інші підкласи. Анімація path маски створює ефект плавної появи контенту через фігуру довільної форми.
Створіть CAGradientLayer та встановіть його mask = CAShapeLayer з потрібним path. Градієнт буде видимий тільки всередині контуру маски. Цей патерн поєднує переваги векторної форми CAShapeLayer та градієнтної заливки CAGradientLayer.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.