Joe Blow Joe Blow - 6 months ago 193
Swift Question

To clear drawing in drawRect shape-layer drawing

Here's a poor way to draw in

drawRect


override func drawRect(rect: CGRect)
{
let circlePath = UIBezierPath(ovalInRect:rect)
let circle = CAShapeLayer()
circle.path = circlePath.CGPath
circle.fillColor = someColor.CGColor
layer.addSublayer(circle)

... and a few more layers like ...
blah.fillColor = someColor.CGColor
layer.addSublayer(blah)
}


If you want to draw something different (example, you toggle between different shapes), in fact you must clear before drawing, or the old material will still be there. I have two questions

(1) Surprisingly setting
clearsContextBeforeDrawing
just does nothing. (I try setting it in code, in Storyboard, everything.) Does
clearsContextBeforeDrawing
just not apply to "layer" -type drawing in drawRect??

(2) In fact the only way I can get it to clear is like this:

override func drawRect(rect: CGRect)
{
// first remove all layers...
if let sublayers = layer.sublayers {
for layer in sublayers {
if layer.isKindOfClass(CAShapeLayer) //(essential)
{ layer.removeFromSuperlayer() }
}
}

let circlePath = UIBezierPath(ovalInRect:rect)
let circle = CAShapeLayer()
circle.path = circlePath.CGPath
circle.fillColor = someColor.CGColor
layer.addSublayer(circle)

... and a few more layers like ...
blah.fillColor = someColor.CGColor
layer.addSublayer(blah)
}


by seemingly removing all the layers.

I cannot believe how hard it was to figure this out: since surely the most common thing you have to do in drawRect is clear the drawing (!!) before drawing, you'd think it would be everywhere in Apple examples etc. So

(2a) give that you do have to remove all the layers, is there a better way? (perhaps forEach, or?)

(2b) is there just some altogether better way to clear the drawing?

Answer

Here's a typical way to draw in drawRect

No, it isn't. It's totally atypical — and totally wrong.

The only thing you should be doing in drawRect is drawing into the current context. For example:

let circlePath = UIBezierPath(ovalInRect:rect)
circlePath.stroke() // draws into the current context

But your code is about something else entirely: you're creating a bunch of additional layers. And that's exactly what's wrong with it, and why you're left scratching your head about the way things seems to behave. If you were doing the correct thing in drawRect, you'd find that indeed whenever it is called, the drawing in the view is cleared. Your drawing in your view is not being cleared because you have no drawing in your view. Everything is in these extra layers.

So, start over. Learn to use drawRect correctly. Either draw directly into the current context, or put your layer-management code elsewhere; but don't combine them by doing your layer-management in drawRect.