CAShapeLayer — este o subclasă CALayer destinată randării hardware a contururilor vectoriale pe GPU. Spre deosebire de CALayer de bază, care lucrează cu imagini raster prin contents, CAShapeLayerrandează forme arbitrare definite prin CGPath fără a crea un buffer offscreen separat. Conform Apple QuartzCore Documentation, 2025, CAShapeLayer utilizează accelerarea hardware pentru netezirea marginilor și suportă animația path, strokeStart și strokeEnd. Este instrumentul standard pentru crearea de pictograme, indicatoare de progres și măști în iOS.
Principalele puncte
CAShapeLayer — este un strat specializat pentru randarea graficii vectoriale, introdus în iOS 3.0. Spre deosebire de CALayer, care stochează o imagine raster, CAShapeLayer stochează descrierea matematică a conturului — CGPath — și îl randează direct pe GPU. Acest lucru permite scalarea formei fără pierdere de calitate și animarea proprietăților sale fără re-randarea conținutului.
Arhitectural, CAShapeLayer moștenește toate capacitățile CALayer: umbre, margini, transformări, măști. Dar adaugă proprietăți specifice pentru lucrul cu contururi: path, fillColor, strokeColor, lineWidth, lineCap, lineJoin, miterLimit, strokeStart, strokeEnd și fillRule. Fiecare dintre aceste proprietăți poate fi animată prin CABasicAnimation.
Conform Apple Documentation, CAShapeLayer utilizează accelerarea hardware la randare. Spre deosebire de desenarea prin Core Graphics în drawRect:, unde CPU randează conturul într-un context raster, CAShapeLayer trimite comenzi direct pe GPU prin Metal. Rezultat — performanță mai mare, în special în timpul animației.
CAShapeLayer este deosebit de util pentru elementele de interfață care își schimbă forma ca răspuns la acțiunile utilizatorului: indicatoare de progres, grafice, pictograme tab-bar, măști pentru animația de apariție a conținutului.
Procesul de randare al CAShapeLayer diferă de CALayer de bază. În loc să stocheze un buffer raster cu pixeli, CAShapeLayer trimite GPU-ului descrierea vertex a conturului — un set de puncte, curbe Bezier și segmente. GPU-ul rasterizează acest contur într-o singură trecere, aplicând culoarea de umplere și contur în etapa shader-ului de fragment.
Această abordare oferă două avantaje: scalare fără pierdere de calitate (grafica vectorială rămâne clară la orice rezoluție) și animație eficientă — la schimbarea path, GPU-ul recalculează doar pixelii modificați, nu întregul ecran.
path — proprietatea centrală a CAShapeLayer, care primește CGPath. Conturul poate conține linii, arcuri, curbe Bezier și dreptunghiuri. Se creează prin UIBezierPath în Swift sau CGPath în Core Graphics. Exemplu: UIBezierPath(roundedRect: rect, cornerRadius: 16).cgPath creează un dreptunghi cu colțuri rotunjite.
Proprietatea lineDashPattern permite crearea liniilor punctate. Primește un array de numere: [6, 3] înseamnă 6 puncte de linie și 3 puncte de spațiu. lineDashPhase stabilește deplasarea modelului și poate fi animată pentru efectul de linie punctată în mișcare.
Alegerea între CAShapeLayer și CALayer de bază depinde de tipul conținutului. Dacă elementul este o zonă dreptunghiulară cu fundal, margine și umbră — este suficient CALayer. Dacă elementul are o formă arbitrară, necesită animație a conturului sau scalare fără pierdere de calitate — CAShapeLayer este preferabil.
Performanța diferă de asemenea. CALayer cu cornerRadius și masksToBounds creează un buffer offscreen pentru decuparea conținutului, ceea ce adaugă 2–5 ms per cadru. CAShapeLayer nu necesită buffer offscreen — decuparea se face la nivelul shader-ului de vertex. Pentru elemente cu măști complexe, CAShapeLayer poate fi de 2–3 ori mai rapid.
Combinarea CAShapeLayer cu alte straturi este un model comun. De exemplu, CAGradientLayer folosește CAShapeLayer ca mască pentru a crea o formă cu umplere gradient. Această abordare oferă atât calitate vectorială, cât și umplere gradient fără pierdere de performanță.
Animația path — una dintre cele mai puternice capacități ale CAShapeLayer. Dacă ambele contururi (inițial și final) au același număr de puncte de control și segmente, Core Animation efectuează o interpolare lină între ele. Rezultat — efectul de morfing al unei forme în alta.
Condiția de compatibilitate path: UIBezierPath inițial și final trebuie să aibă aceeași structură — numărul de apeluri moveTo, addLine, addCurve și addQuadCurve trebuie să coincidă. Dacă structura diferă, animația va fi instantanee, fără interpolare.
Conform Apple WWDC 2024, combinația strokeStart și strokeEnd este cel mai frecvent utilizat model de animație CAShapeLayer. Animația strokeEnd de la 0.0 la 1.0 creează efectul de desenare a conturului de la început la sfârșit. Este folosit în indicatoarele de încărcare, animațiile de apariție a graficelor și tranzițiile personalizate între ecrane.
Pentru a crea un efect de pulsație, animează simultan strokeEnd și opacity cu funcții de timing diferite. Pentru efectul de linie punctată în mișcare, animează lineDashPhase cu lineDashPattern fix. Ambele tehnici se implementează prin CABasicAnimation fără participarea CPU.
CAShapeLayer este adesea folosit ca mask pentru alte straturi. De exemplu, pentru animația de apariție a conținutului: la început mask.path este un dreptunghi de dimensiune zero, la final — dimensiunea completă. Core Animation interpolează path, iar conținutul se dezvăluie lin.
Acest model este folosit în iOS pentru animația de desfășurare a cardurilor, apariția ferestrelor modale și tranzițiile între view controllere. Spre deosebire de animația alpha, animația prin masca CAShapeLayer oferă un efect mai natural de apariție cu o formă arbitrară.
Primul exemplu — crearea unui indicator circular de progres cu animația strokeEnd. Acesta este un element UI standard care demonstrează capacitățile principale ale 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
}
// Animație până la 75%
progressLayer.animateStrokeEnd(to: 0.75, duration: 1.5)
Al doilea exemplu — animația de morfing între cerc și pătrat. Ambele UIBezierPath au o structură compatibilă:
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")
Al treilea exemplu — mască cu CAShapeLayer pentru animația de apariție a conținutului. Masca se extinde de la centru spre margini:
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
}
Întrebări frecvente
drawRect folosește CPU pentru randarea într-un context raster — este lent pentru animații. CAShapeLayer randează conturul pe GPU, oferind 60 FPS la animația path, strokeEnd și transformări. În plus, CAShapeLayer nu necesită re-randare la schimbarea dimensiunilor — forma se scalează vectorial.
Animația lină a path necesită aceeași structură a UIBezierPath inițial și final. Numărul de apeluri moveTo, addLine, addCurve trebuie să coincidă. Folosește UIBezierPath cu același număr de segmente pentru forma inițială și finală.
Setează lineDashPattern — un array de numere care alternează lungimea liniei și a spațiului. De exemplu, [8, 4] creează linii de 8pt cu spații de 4pt. Pentru linia punctată în mișcare, animează lineDashPhase prin CABasicAnimation cu o funcție de timing liniară.
Da, acesta este unul dintre scenariile principale. Atribuie CAShapeLayer proprietății mask a oricărui CALayer, inclusiv CAGradientLayer și alte subclase. Animația path a măștii creează efectul de apariție lină a conținutului printr-o formă arbitrară.
Creează CAGradientLayer și setează mask acestuia = CAShapeLayer cu path-ul dorit. Gradientul va fi vizibil doar în interiorul conturului măștii. Acest model combină avantajele formei vectoriale CAShapeLayer cu umplerea gradient a CAGradientLayer.
Rezumat
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și