CAShapeLayer is a subclass of CALayer designed for hardware-accelerated rendering of vector paths on the GPU. Unlike the basic CALayer, which works with raster images via contents, CAShapeLayer renders arbitrary shapes defined through CGPath without creating a separate offscreen buffer. According to Apple QuartzCore Documentation, 2025, CAShapeLayer uses hardware acceleration for anti-aliasing and supports path, strokeStart and strokeEnd animation. It is a standard tool for creating icons, progress indicators and masks in iOS.
Key Takeaways
CAShapeLayer is a specialized layer for rendering vector graphics, introduced in iOS 3.0. Unlike CALayer, which stores a raster image, CAShapeLayer stores a mathematical description of a path — CGPath — and renders it directly on the GPU. This allows the shape to be scaled without quality loss and its properties to be animated without redrawing the contents.
Architecturally, CAShapeLayer inherits all capabilities of CALayer: shadows, borders, transforms, masks. But it adds specific properties for working with paths: path, fillColor, strokeColor, lineWidth, lineCap, lineJoin, miterLimit, strokeStart, strokeEnd and fillRule. Each of these properties can be animated via CABasicAnimation.
According to Apple Documentation, CAShapeLayer uses hardware acceleration for rendering. Unlike drawing via Core Graphics in drawRect:, where the CPU renders the path into a raster context, CAShapeLayer sends commands directly to the GPU via Metal. The result is higher performance, especially during animation.
CAShapeLayer is particularly useful for interface elements that change shape in response to user actions: progress indicators, charts, tab bar icons, masks for content reveal animations.
The rendering process of CAShapeLayer differs from the basic CALayer. Instead of storing a raster buffer with pixels, CAShapeLayer passes a vertex description of the path to the GPU — a set of points, Bezier curves and line segments. The GPU rasterizes this path in a single pass, applying fill and stroke colors at the fragment shader stage.
This approach offers two advantages: scaling without quality loss (vector graphics remain sharp at any resolution) and efficient animation — when path changes, the GPU recalculates only the changed pixels, not the entire screen.
path — the central property of CAShapeLayer, accepting a CGPath. The path can contain lines, arcs, Bezier curves and rectangles. It is created via UIBezierPath in Swift or CGPath in Core Graphics. Example: UIBezierPath(roundedRect: rect, cornerRadius: 16).cgPath creates a rounded rectangle.
The lineDashPattern property allows creating dashed lines. It accepts an array of numbers: [6, 3] means 6 points of dash and 3 points of gap. lineDashPhase sets the pattern offset and can be animated for a moving dash effect.
The choice between CAShapeLayer and the basic CALayer depends on the type of content. If the element is a rectangular area with background, border and shadow — CALayer is sufficient. If the element has an arbitrary shape, requires path animation or lossless scaling — CAShapeLayer is preferable.
Performance also differs. CALayer with cornerRadius and masksToBounds creates an offscreen buffer for content clipping, adding 2–5 ms per frame. CAShapeLayer does not require an offscreen buffer — clipping is performed at the vertex shader level. For elements with complex masks, CAShapeLayer can be 2–3 times faster.
Combining CAShapeLayer with other layers is a common pattern. For example, CAGradientLayer uses CAShapeLayer as a mask to create a shape with gradient fill. This approach provides both vector quality and gradient filling without performance loss.
Path animation is one of the most powerful features of CAShapeLayer. If both paths (initial and final) have the same number of control points and segments, Core Animation performs smooth interpolation between them. The result is a morphing effect from one shape to another.
Path compatibility condition: the initial and final UIBezierPath must have the same structure — the number of moveTo, addLine, addCurve and addQuadCurve calls must match. If the structure differs, the animation will be instantaneous, without interpolation.
According to Apple WWDC 2024, the combination of strokeStart and strokeEnd is the most commonly used CAShapeLayer animation pattern. Animating strokeEnd from 0.0 to 1.0 creates a path drawing effect from start to end. This is used in loading indicators, chart reveal animations and custom screen transitions.
To create a pulsing effect, animate strokeEnd and opacity simultaneously with different timing functions. For a marching ants effect, animate lineDashPhase with a fixed lineDashPattern. Both techniques are implemented via CABasicAnimation without CPU involvement.
CAShapeLayer is often used as a mask for other layers. For example, for content reveal animation: initially, mask.path is a zero-size rectangle, and the final is full size. Core Animation interpolates the path, and the content reveals smoothly.
This pattern is used in iOS for card expansion animation, modal window presentation and view controller transitions. Unlike alpha animation, CAShapeLayer mask animation provides a more natural reveal effect with arbitrary shapes.
The first example — creating a circular progress indicator with strokeEnd animation. This is a standard UI element that demonstrates the core capabilities of 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
}
// Animate to 75%
progressLayer.animateStrokeEnd(to: 0.75, duration: 1.5)
The second example — morphing animation between a circle and a square. Both UIBezierPath have a compatible structure:
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")
The third example — a CAShapeLayer mask for content reveal animation. The mask expands from the center to the edges:
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
}
Frequently Asked Questions
drawRect uses the CPU to render into a raster context — this is slow for animations. CAShapeLayer renders the path on the GPU, delivering 60 FPS during path, strokeEnd and transform animations. Additionally, CAShapeLayer does not require redrawing when resizing — the shape scales vectorially.
Smooth path animation requires identical structure of the initial and final UIBezierPath. The number of moveTo, addLine, addCurve calls must match. Use UIBezierPath with the same number of segments for the initial and final shapes.
Set lineDashPattern — an array of numbers alternating dash and gap lengths. For example, [8, 4] creates 8pt dashes with 4pt gaps. For a marching ants effect, animate lineDashPhase via CABasicAnimation with a linear timing function.
Yes, this is one of the main use cases. Assign CAShapeLayer to the mask property of any CALayer, including CAGradientLayer and other subclasses. Animating the mask path creates a smooth content reveal effect through an arbitrary-shaped figure.
Create a CAGradientLayer and set its mask = CAShapeLayer with the desired path. The gradient will be visible only inside the mask path. This pattern combines the vector shape advantages of CAShapeLayer and the gradient fill of CAGradientLayer.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also