CAShapeLayer — to podklasa CALayer przeznaczona do sprzętowego renderowania wektorowych konturów na GPU. W przeciwieństwie do podstawowego CALayer, który działa na obrazach rastrowych przez contents, CAShapeLayer renderuje dowolne kształty zadane przez CGPath bez tworzenia osobnego bufora offscreen. Według Apple QuartzCore Documentation, 2025, CAShapeLayer wykorzystuje sprzętowe przyspieszenie do wygładzania krawędzi i obsługuje animację path, strokeStart i strokeEnd. To standardowe narzędzie do tworzenia ikon, wskaźników postępu i masek w iOS.
Najważniejsze
CAShapeLayer — to specjalizowana warstwa do renderowania grafiki wektorowej, wprowadzona w iOS 3.0. W przeciwieństwie do CALayer, który przechowuje obraz rastrowy, CAShapeLayer przechowuje matematyczny opis konturu — CGPath — i renderuje go bezpośrednio na GPU. Umożliwia to skalowanie kształtu bez utraty jakości i animowanie jego właściwości bez przerysowywania zawartości.
Architektonicznie CAShapeLayer dziedziczy wszystkie możliwości CALayer: cienie, granice, transformacje, maski. Ale dodaje specyficzne właściwości do pracy z konturami: path, fillColor, strokeColor, lineWidth, lineCap, lineJoin, miterLimit, strokeStart, strokeEnd i fillRule. Każda z tych właściwości może być animowana przez CABasicAnimation.
Według Apple Documentation, CAShapeLayer wykorzystuje sprzętowe przyspieszenie podczas renderowania. W przeciwieństwie do rysowania przez Core Graphics w drawRect:, gdzie CPU renderuje kontur w kontekście rastrowym, CAShapeLayer wysyła polecenia bezpośrednio na GPU przez Metal. Rezultat — wyższa wydajność, szczególnie podczas animacji.
CAShapeLayer jest szczególnie przydatny dla elementów interfejsu, które zmieniają kształt w odpowiedzi na działania użytkownika: wskaźniki postępu, wykresy, ikony tab-bara, maski do animacji pojawiania się treści.
Proces renderowania CAShapeLayer różni się od podstawowego CALayer. Zamiast przechowywać bufor rastrowy z pikselami, CAShapeLayer przekazuje GPU wierzchołkowy opis konturu — zbiór punktów, krzywych Beziera i odcinków. GPU rasteryzuje ten kontur w jednym przebiegu, stosując kolor wypełnienia i obrysu na etapie shadera fragmentów.
To podejście daje dwie zalety: skalowanie bez utraty jakości (grafika wektorowa pozostaje ostra przy każdej rozdzielczości) i efektywną animację — przy zmianie path GPU przelicza tylko zmienione piksele, a nie cały ekran.
path — centralna właściwość CAShapeLayer, przyjmująca CGPath. Kontur może zawierać linie, łuki, krzywe Beziera i prostokąty. Tworzony przez UIBezierPath w Swift lub CGPath w Core Graphics. Przykład: UIBezierPath(roundedRect: rect, cornerRadius: 16).cgPath tworzy zaokrąglony prostokąt.
Właściwość lineDashPattern pozwala tworzyć linie przerywane. Przyjmuje tablicę liczb: [6, 3] oznacza 6 punktów kreski i 3 punkty przerwy. lineDashPhase określa przesunięcie wzoru i może być animowany dla efektu poruszającej się linii przerywanej.
Wybór między CAShapeLayer a podstawowym CALayer zależy od typu treści. Jeśli element to prostokątny obszar z tłem, granicą i cieniem — wystarczy CALayer. Jeśli element ma dowolny kształt, wymaga animacji konturu lub skalowania bez utraty jakości — CAShapeLayer jest preferowany.
Wydajność również się różni. CALayer z cornerRadius i masksToBounds tworzy bufor offscreen do przycinania zawartości, co dodaje 2–5 ms na klatkę. CAShapeLayer nie wymaga bufora offscreen — przycinanie odbywa się na poziomie shadera wierzchołków. Dla elementów ze złożoną maską CAShapeLayer może być 2–3 razy szybszy.
Łączenie CAShapeLayer z innymi warstwami to powszechny wzorzec. Na przykład CAGradientLayer używa CAShapeLayer jako maski do tworzenia kształtu z wypełnieniem gradientowym. Takie podejście daje zarówno jakość wektorową, jak i wypełnienie gradientowe bez utraty wydajności.
Animacja path — jedna z najpotężniejszych możliwości CAShapeLayer. Jeśli oba kontury (początkowy i końcowy) mają tę samą liczbę punktów kontrolnych i segmentów, Core Animation wykonuje płynną interpolację między nimi. Rezultat — efekt morfingu jednego kształtu w drugi.
Warunek zgodności path: początkowy i końcowy UIBezierPath muszą mieć tę samą strukturę — liczba wywołań moveTo, addLine, addCurve i addQuadCurve musi się zgadzać. Jeśli struktura się różni, animacja będzie natychmiastowa, bez interpolacji.
Według Apple WWDC 2024, kombinacja strokeStart i strokeEnd to najczęściej używany wzorzec animacji CAShapeLayer. Animacja strokeEnd od 0.0 do 1.0 tworzy efekt rysowania konturu od początku do końca. Jest używany we wskaźnikach ładowania, animacjach pojawiania się wykresów i niestandardowych przejściach między ekranami.
Aby stworzyć efekt pulsowania, animuj jednocześnie strokeEnd i opacity z różnymi funkcjami timingowymi. Dla efektu biegnącej linii przerywanej animuj lineDashPhase przy stałym lineDashPattern. Oba triki są realizowane przez CABasicAnimation bez udziału CPU.
CAShapeLayer jest często używany jako mask dla innych warstw. Na przykład do animacji pojawiania się treści: na początku mask.path to prostokąt o zerowym rozmiarze, końcowy — pełny rozmiar. Core Animation interpoluje path, a treść odkrywa się płynnie.
Ten wzorzec jest używany w iOS do animacji rozwijania kart, pojawiania się okien modalnych i przejść między view controllerami. W przeciwieństwie do animacji alpha, animacja przez maskę CAShapeLayer daje bardziej naturalny efekt pojawiania się z dowolnym kształtem.
Pierwszy przykład — tworzenie okrągłego wskaźnika postępu z animacją strokeEnd. To standardowy element UI, który demonstruje główne możliwości 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
}
// Animacja do 75%
progressLayer.animateStrokeEnd(to: 0.75, duration: 1.5)
Drugi przykład — animacja morfingu między kołem a kwadratem. Oba UIBezierPath mają zgodną strukturę:
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")
Trzeci przykład — maska z CAShapeLayer do animacji pojawiania się treści. Maska rozszerza się od środka do krawędzi:
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
}
Często zadawane pytania
drawRect używa CPU do renderowania w kontekście rastrowym — jest wolne dla animacji. CAShapeLayer renderuje kontur na GPU, co daje 60 FPS przy animacji path, strokeEnd i transformacji. Ponadto CAShapeLayer nie wymaga przerysowania przy zmianie rozmiarów — kształt skaluje się wektorowo.
Płynna animacja path wymaga jednakowej struktury początkowego i końcowego UIBezierPath. Liczba wywołań moveTo, addLine, addCurve musi się zgadzać. Używaj UIBezierPath z tą samą liczbą segmentów dla kształtu początkowego i końcowego.
Ustaw lineDashPattern — tablicę liczb naprzemiennie określających długość kreski i przerwy. Na przykład [8, 4] tworzy kreski 8pt z przerwami 4pt. Dla biegnącej linii przerywanej animuj lineDashPhase przez CABasicAnimation z liniową funkcją timingową.
Tak, to jeden z głównych scenariuszy. Przypisz CAShapeLayer do właściwości mask dowolnego CALayer, w tym CAGradientLayer i innych podklas. Animacja path maski tworzy efekt płynnego pojawiania się treści przez kształt o dowolnej formie.
Utwórz CAGradientLayer i ustaw jego mask = CAShapeLayer z odpowiednim path. Gradient będzie widoczny tylko wewnątrz konturu maski. Ten wzorzec łączy zalety wektorowego kształtu CAShapeLayer z wypełnieniem gradientowym CAGradientLayer.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również