Skip to content

RHView and Delegate Drawing…

The delegate drawing pattern implemented by a RHView is not a decorator pattern. It is a simple delegate pattern. I intend you, though, to use it somewhat differently than a traditional delegate. A traditional delegate would be responsible for drawing directly in the RHView. And, of course, you can use it that way. I use it differently though.

I create all of my drawing commands as methods of the RHView. This allows complex graphics commands to be encapsulated in the view. The delegate becomes a drawing coordinator and does not a directly draw in the view.

Here is a simple example — a subclass of RHView to draw a graph:

#import "RHView.h"
@class Curve;
@interface GraphView : RHView {
@private Curve *curve; }
@property (retain, nonatomic) Curve *curve;
- (void) drawBorder: (CGRect) rect inContext: (CGContextRef) context; - (void) drawCurve: (CGRect) rect inContext: (CGContextRef) context;

It has a single instance variable — a curve. And it has two drawing methods — -drawBorder:inContext: and -drawCurve:inContext:. The delegate object only needs to implement the RHViewDelegate protocol.

Here is a sample delegate implementation:

- (void) drawView: (GraphView *) view inRect: (CGRect) rect inContext: (CGContextRef) context {
[view drawCurve: rect inContext: context]; [view drawBorder: rect inContext: context];

All drawing commands are encapsulated in the subclass of RHView. Your view controller is, in my opinion, the natural delegate of the RHView. You then just create new view controllers to coordinate the new behavior. It is totally congruent with the MVC pattern.

This pattern really cleaned up my code. I hope it cleans yours up too.

An aside: the RHViewDelegate is the key mechanism needed to initiate a decorator drawing pattern.

Coordinate Systems and the iPhone…

Interesting iPhone apps draw custom views/widgets. Every platform though makes assumptions that will be good for some applications and bad for others. As the Mac OS X evolved into iPhone OS, one of the graphics assumptions changed — the handedness of the drawing coordinate system. In Mac OS X’s case, the right handed coordinate system was a natural match to traditional graphing which we all learned in high school algebra. iPhone OS uses a left handed coordinate system. The left handed system is very good for drawing text. The big difference is in the direction of the y-axis. In a right handed system, the y-axis points up; a left handed y-axis points down. If you want to draw scatter plots, then a right handed system is just easier to use.

The graphics system is called Quartz 2D. It supports general purpose transformations of every graphics action. This is how the handedness can switch between the Mac and the iPhone — Quartz 2D made it easy. Hence, to return to a right handed system, we need to use this transformation system to flip the coordinate system. We will also need to move the origin of the view from the top-left to the bottom-left corner. Two tasks: flip the coordinate system and translate the origin.

Each view in Cocoa Touch draws in exactly one method: -drawRect:. Our code needs to intercept this method and transform the coordinate system. Also, because there is a common design pattern in Cocoa Touch, delegation, we should support that too. Finally, because of the ways views are implemented in Cocoa Touch, as CALayers, our class needs to provide some methods to make it easy for layers to be rendered in our new coordinate system.

Two tasks:

  • Flip the coordinate system.
  • Translate the origin.

Three requirements:

  • Intercept -drawRect:
  • Implement a delegate pattern.
  • Provide a utility method for layer drawing.

The Right Hand View Header:

@interface RHView : UIView {
	id <RHViewDelegate> delegate;
@property (assign, nonatomic) id <RHViewDelegate> delegate;
// Abstract methods. This must be overridden. - (void) drawRect: (CGRect) rect inContext: (CGContextRef) context;
// Concrete methods. - (CGContextRef) flipContext: (CGContextRef) context; @end

Starting at the top:

This class inherits from UIView, the standard parent class to all views in Cocoa Touch. It’s only instance variable is a pointer to a delegate that implements the RHViewDelegate protocol. As with almost all other delegate instance variables in Cocoa Touch, the delegate is assigned and not retained. It is up to the delegate to keep from being reaped.

This class has two public methods. The first method, -drawRect:inContext:, is an abstract method. It must be overridden by your subclass to draw anything. Or you must use the delegate. You cannot use both at the same time.

The second method, -flipContext:, is a utility method to transform a graphics context between coordinate systems. You should not override this method.

The alternate way to draw is with a delegate that implements the RHViewDelegate protocol.

@protocol RHViewDelegate
// Draw contents into the given view, using the given bounds and context. - (void) drawView: (RHView *) view inRect: (CGRect) rect inContext: (CGContextRef) context;

Because a delegate is unrelated to the view, you need to pass the view to the delegate along with the rectangle that needs to be filled in and the flipped context: -drawView:inRect:inContext:.

How does RHView work?:

I’ll start with simpler of the 2 methods: -flipContext:.

- (CGContextRef) flipContext: (CGContextRef) context {
    // Get the current transformation matrix.
    CGAffineTransform ctm = CGContextGetCTM(context);
    // Toggle the origin's position between the bottom-left and top-left.
    CGAffineTransformTranslate(ctm, 0.0, self.bounds.size.height);
    // Flip the handedness of the coordinate system.
    CGAffineTransformScale(ctm, 1.0, -1.0);
    // Apply the new coordinate system to the CGContext.
    CGContextConcatCTM(context, ctm);
    return context;
} // flipContext:

This method is rather straightforward. It implements our two tasks: flipping and translation. The key thing to recognize is that the new origin is always at the “top” of the current view. In a left handed system, the top-left point is the same as the bottom-left of a right handed system. And this is true, vice versa. The second transform, the CGAffineTransformScale, changes the sign of the y-axis. It changes the handedness of the system. That really is all that is required.

The RHView -drawRect: method has to do both the transformation and delegate dispatch. Unlike a normal UIView, you do not override -drawRect:. Because it is responsible for managing the coordinate system, it has to get the first crack at drawing the view. Hence, you need to override the -drawRect:inContext: method. The only difference between the two methods is that you will use the provided context instead of the default context. Other than that, everything about the new method is treated identically to the classical -drawRect: method. (A note: -drawRect:inContext: is using the same naming pattern as CALayer‘s delegate method -drawLayer:inContext:.)

- (void) drawRect: (CGRect) rect {
    // Convert the coordinate system to be what Quartz 2D expects.
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGAffineTransform ctm = CGContextGetCTM(context);
    // Translate the origin to the bottom left.
    CGAffineTransformTranslate(ctm, 0.0, self.bounds.size.height);
    // Flip the handedness of the coordinate system back to right handed.
    CGAffineTransformScale(ctm, 1.0, -1.0);
    // Convert the update rectangle to the new coordiante system.
    CGRect xformRect = CGRectApplyAffineTransform(rect, ctm);
    // Apply the new coordinate system to the CGContext.
    CGContextConcatCTM(context, ctm);
    if (delegate != nil) {
        // The delegate gets the first crack at rendering the view.
        [delegate drawView: self inRect: xformRect inContext: context];
    } else {
        [self drawRect: xformRect inContext: context];
} // drawRect:

-drawRect: has to do four tasks: 1) get the current graphics context; 2) translate and flip the coordinate system; 3) transform the drawing rectangle; 4) dispatch to either the delegate or subclass.

Getting the Code:

I’ve released this code into the public domain.
It is available here:


The iRush: Programming the iPhone…

’09ers versus the ’49ers: the only differences are the lack of dirt and hookers.

The iRush has been underway for 18 or so months now. The facts are clear. This is a great market to experiment with product design and business models. It is also where many small, “long tail” businesses are being formed — including ours.

Yet, this wonder of social computing, the iPhone, has been woefully underexploited. The 50K+ apps are mostly uninspired but polished attempts to bring desktop activities to the phone. The phone is a bridge device between the social realm and the intimate realm. Therein lies opportunity.

About the DDG blog…

This blog is a place for me, Andrew Donoho, to write about technical strategy, design and programming/development issues. I’ll be publishing code, primarily for the iPhone. I’ll discuss the strategic implications of technology developments. And I’ll be discussing design in both the user interface and the code.

I hope you enjoy it.