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 ms на кадър. CAShapeLayer не изисква offscreen буфер — изрязването става на ниво vertex шейдър. За елементи със сложна маска, 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 с различни функции на времето. За ефект на движеща се пунктирана линия, анимирайте lineDashPhase при фиксиран lineDashPattern. И двете техники се реализират чрез CABasicAnimation без участие на CPU.
CAShapeLayer често се използва като mask за други слоеве. Например, за анимация на появяване на съдържание: в началото mask.path е правоъгълник с нулев размер, в края — пълен размер. Core Animation интерполира path и съдържанието се разкрива плавно.
Този модел се използва в iOS за анимация на разгъване на карти, появяване на модални прозорци и преходи между view controller-и. За разлика от анимацията 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 с линейна функция на времето.
Да, това е един от основните сценарии. Присвоете CAShapeLayer на свойството mask на произволен CALayer, включително CAGradientLayer и други подкласове. Анимацията на path на маската създава ефект на плавно появяване на съдържание чрез фигура с произволна форма.
Създайте CAGradientLayer и задайте неговия mask = CAShapeLayer с желания path. Градиентът ще бъде видим само вътре в контура на маската. Този модел съчетава предимствата на векторната форма на CAShapeLayer с градиентното запълване на CAGradientLayer.
Резюме
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също