1

I'm trying to understand CAShapeLayer and where it shines. It seems like the main advantage is the shape style properties that you can conveniently access:

let shapeLayer = CAShapeLayer()
shapeLayer.frame = CGRect(origin: CGPoint(x: 100, y: 100), size: .init(width: 200, height: 200))
shapeLayer.fillColor = .none
shapeLayer.lineWidth = 5
shapeLayer.strokeColor = UIColor.blue.cgColor

let path = UIBezierPath()
let withCenter = CGPoint(x: 100, y: 100)
let radius = CGFloat(100)
let startAngle = CGFloat(0.0)
let endAngle = CGFloat(CGFloat.pi * 2)

path.addArc(withCenter: withCenter, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true)

shapeLayer.path = path.cgPath

But, I think the advantage ends there. It rather limits your ability set the context to multiple states like colors for different paths and requires a new CAShapeLayer. I can override the drawing method or use the delegate, but that's be doing the same thing as CALayer in that case.

On the other hand, with CALayer:

let layer = CALayer()
layer.frame = CGRect(origin: CGPoint(x: 100, y: 300), size: .init(width: 200, height: 200))
let renderer = UIGraphicsImageRenderer(size: CGSize(width: 200, height: 200))
let image = renderer.image { (_) in
    let path = UIBezierPath()
    let withCenter = CGPoint(x: 100, y: 100)
    let radius = CGFloat(100)
    let startAngle = CGFloat(0.0)
    let endAngle = CGFloat(CGFloat.pi * 2)
    
    path.lineWidth = 5
    UIColor.blue.setStroke()
    path.addArc(withCenter: withCenter, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true)
    path.stroke()
}

layer.contents = image.cgImage

you can set multiple stroke colors for different paths. Obviously, you can set the CAShapeLayer's contents in a similar manner, but I want to take advantage of CAShapeLayer's API's that CALayer doesn't offer.

Are the shape style properties the main reason for CAShapeLayer?

Kevvv
  • 3,655
  • 10
  • 44
  • 90
  • 1
    Sure, it's just a convenience, like CATextLayer etc., for one simple case, a bezier path / CGPath with a stroke and fill color. If you don't want to use it, don't. If you do, do. – matt Dec 03 '20 at 00:50
  • 2
    However, one thing it can do that you would find very difficult to imitate is when you animate its properties, especially the `strokeEnd`. Indeed, you probably would not know how to do with a bezier path what a shape layer does with a `strokeStart` and `strokeEnd`. – matt Dec 03 '20 at 00:52
  • 2
    You won't accomplish the same results scaling an image instead of a shape layer – Leo Dabus Dec 03 '20 at 00:53
  • 2
    IIRC, the shape drawn by a `CAShapeLayer` is vector graphics, whereas the `CALayer`, the way you did it, draws a bitmap image. Try scaling both layers using `CGAffineTransform`. – Sweeper Dec 03 '20 at 00:53
  • 1
    @matt ah ok, by the virtue of the fact that the key path exists, we can animate those properties, altho not all of them. I was able to animate the `strokeStart`, `strokeEnd`, `lineWidth`, `miterLimit`, and even the `path` itself. – Kevvv Dec 03 '20 at 01:30
  • You don't have to use the key path; with layers, just setting a property is automatically animated. But yes, of course you _can_ use CABasicAnimation or whatever – matt Dec 03 '20 at 01:54

1 Answers1

1

For me, the big advantage to CAShapeLayer is animation. As Matt says, you can animate strokeStart and/or strokeEnd to cause a shape to either appear like it's being drawn with a pen, or disappear. You can also use animate strokeStart and/or strokeEnd to create a variety of different "wipe" animations.

Here is a link to a post I wrote using animations to strokeEnd to create a "clock wipe" animation:

How do you achieve a "clock wipe"/ radial wipe effect in iOS?

You can also animate the path that's installed into the shape layer. As long as the starting and ending paths have the same number of control points, the system creates a smooth, elegant looking animation. There are some tricks to making this work correctly however. Arc animations don't work as expected if you change the arc angle during an animation, because internally arcs are composed of different numbers of cubic Bezier curves.

Check out my projects RandomBlobs and TrochoidDemo on Github for examples of animating a path's control points.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • Apart from `CAShapeLayer`'s animation capability, is it correct to think that it's the only way to "draw" with Core Animation? For example, if I were to draw with `CALayer`, which doesn't have the `path` property like `CAShapeLayer` does, I'll either have to subclass and override `draw` or use the `CALayerDelegate`, both of which uses Core Graphics. Or I can use the `contents` property like the one in my question, which is not Core Animation. – Kevvv Dec 03 '20 at 06:08
  • What I mean by "drawing with Core Animation" is CA uses GPU to composite the vector graphics whereas Core Graphics uses the CPU. So I'm wondering what's happening when `UIBezierPath` draws on `CAShapeLayer`. – Kevvv Dec 03 '20 at 06:10
  • In general, overriding a view's/layer's draw method means you're using CG calls on the CPU, not GPU-accelerated graphics. I don't know of a GPU-accelerated way to render paths other than using shape layers. It becomes even more important when doing animation. Core Animation takes care of interpolating between the start and end state of the animation and generating all the frames between, and generates those frames very efficiently. – Duncan C Dec 03 '20 at 16:53