CAShapeLayer is an interesting new subclass of CALayer available in 3.0 and now 10.6, and it’s got some pretty cool features.
A shapelayer represents an arbitrary shape that gets rendered onscreen. The shape is defined by its path property which takes a CGPathRef. The path can be anything. So in a sense it’s a freeform drawing API brought up to the Core Animation framework level and available to you in your layering and compositing hierarchy.
Interestingly, the shape isn’t rendered into the layer’s contents. In fact, the layer’s contents aren’t rendered at all, only the shape is. The documentation states that “the shape is composited between the layer’s contents and its first sublayer”. So the layer is in a sense just a container for the shape. You can set all of its standard CALayer properties, like contents and backgroundColor, but they get zeroed out at render time.
Solidifying this distinction, CAShapeLayer has its own set of properties for describing visual attributes following the nomenclature of working with a graphics context. So if you want to create a shapelayer, you’re looking for fillColor and strokeColor. Setting up a shapelayer is pretty easy and would look something like this.
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = crazyAwesomePath;
UIColor *fillColor = [UIColor colorWithHue:0.625 saturation:0.4 brightness:0.5 alpha:1.0];
shapeLayer.fillColor = fillColor.CGColor;
UIColor *strokeColor = [UIColor colorWithHue:0.625 saturation:0.4 brightness:0.9 alpha:1.0];
shapeLayer.strokeColor = strokeColor.CGColor;
shapeLayer.lineWidth = 2.0;
shapeLayer.fillRule = kCAFillRuleNonZero;
The fillRule tells the rendering engine how to deal with intersecting paths and NonZero just means fill everything inside the path. CAShapeLayer also comes with a set of properties for describing intricate line dash patterns as well as properties for how lines end and corners meet.
The Crazy Part
Since it’s a layer, everything is animatable. Literally everything, even the path property. I couldn’t imagine what this meant at first, and decided it must cross-fade from one shape to another or something. But no, indeed the Core Animation engine interpolates values from the current path to the new path and morphs the shape from one to the other. It’s pretty mind-blowing.
To get a smooth animation from one path to another you want the same number of subpaths and specifically the same number of points, because even though Core Animation doesn’t speak in vertices, that’s really what it’s working with. If you have a discrepancy from one path to another the interpolation engine drops to the lowest common denominator of points and creates its own subpaths to complete the shape, which gives some unexpected results.
Setting up a continuous path animation would look something like this..
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@”path”];
animation.duration = 2.0;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.repeatCount = 1e100f;
animation.autoreverses = YES;
animation.fromValue = (id)initialPath;
animation.toValue = (id)newPath;
[shapeLayer addAnimation:animation forKey:@”animatePath”];
I put together a couple examples. The first one uses basic shapes that you actually get for free with layers, and just demonstrates CAShapeLayer’s ability to interpolate between them. The second is a little more interesting, using complex polygons sharing only the same number of points and subpaths.
You can check out a video of it in action here.