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:

  1. How to decide which approach to use? Is there a use case for each one?
  2. If I implement drawLayer:inContext:, it is called (and drawRect isn't, at least as far as putting a breakpoint can tell), even if I don't assign my view as the CALayer 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 if drawLayer: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:

drawRect: stacktrace

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