iOS: Using UIView's 'drawRect:' vs. its layer's delegate 'drawLayer:inContext:'
I have a class which is a subclass of UIView
. I am able to draw stuff inside the view either by implementing the drawRect
method, or by implementing drawLayer:inContext:
which is a delegate method of CALayer
.
I have two questions:
- How to decide which approach to use? Is there a use case for each one?
-
If I implement
drawLayer:inContext:
, it is called (anddrawRect
isn't, at least as far as putting a breakpoint can tell), even if I don't assign my view as theCALayer
delegate by using:[[self layer] setDelegate:self];
how come the delegate method is called if my instance is not defined to be the layer's delegate? and what mechanism prevents
drawRect
from being called ifdrawLayer:inContext:
is called?
Solution 1:
How to decide which approach to use? Is there a use case for each one?
Always use drawRect:
, and never use a UIView
as the drawing delegate for any CALayer
.
how come the delegate method is called if my instance is not defined to be the layer's delegate? and what mechanism prevents drawRect from being called if
drawLayer:inContext:
is called?
Every UIView
instance is the drawing delegate for its backing CALayer
. That's why [[self layer] setDelegate:self];
seemed to do nothing. It's redundant. The drawRect:
method is effectively the drawing delegate method for the view's layer. Internally, UIView
implements drawLayer:inContext:
where it does some of its own stuff and then calls drawRect:
. You can see it in the debugger:
This is why drawRect:
was never called when you implemented drawLayer:inContext:
. It's also why you should never implement any of the CALayer
drawing delegate methods in a custom UIView
subclass. You should also never make any view the drawing delegate for another layer. That will cause all sorts of wackiness.
If you are implementing drawLayer:inContext:
because you need to access the CGContextRef
, you can get that from inside of your drawRect:
by calling UIGraphicsGetCurrentContext()
.
Solution 2:
drawRect
should only be implemented when absolutely needed. The default implementation of drawRect
includes a number of smart optimizations, like intelligently caching the view's rendering. Overriding it circumvents all of those optimizations. That's bad. Using the layer drawing methods effectively will almost always outperform a custom drawRect
. Apple uses a UIView
as the delegate for a CALayer
often - in fact, every UIView is the delegate of it's layer. You can see how to customize the layer drawing inside a UIView in several Apple samples including (at this time) ZoomingPDFViewer.
While the use of drawRect
is common, it's a practice that has been discouraged since at least 2002/2003, IIRC. There aren't many good reasons left to go down that path.
Advanced Performance Optimization on iPhone OS (slide 15)
Core Animation Essentials
Understanding UIKit Rendering
Technical Q&A QA1708: Improving Image Drawing Performance on iOS
View Programming Guide: Optimizing View Drawing