Protocols & Custom Delegates
Day 41
Understanding delegation in iOS vital when creating an app of any complexity, however I found it difficult to find a good, concise explanation of what it is during the early stages of my learning. In this post I’ll try to explain exactly what it is and provide some steps for creating your own custom delegates.
This is not intended to be an exhaustive tutorial, just an introduction to the concepts of what delegation is. I found that once I understood exactly what delegation was and why I might want to use it, Googling the answers became much easier.
What Are Delegates?
To understand why delegation is important, you first need to consider the Model View Controller design pattern (I’ll assume you’re familiar with it already if not, see lecture 1 of Paul Hegarty’s iOS Development series on iTunes U). The most important rule when it comes to MVC is that each area is responsible for its own domain, that is:
- The model handles the data
- The view handles things that appear on screen
- The controller sits between the model and view and passes data from the model to the view
Critically, the pathway from the controller to the view should always be one-way – in fact, the view should not even know the controller exists. For example, if you created an iPhone application with a controller and a view, you should be able to swap out the view for an iPad-specific file while still using the same controller. After all, the controller only cares about what data it can get from the model, and how that data should be structured before it is sent over to a view.
Now, on the Web this is an easy concept to maintain because once the page has been sent to the browser it no longer has access to the controller anyway. In software, however, there are often times when the view needs extra information from the controller that it didn’t need before.
You don’t want your view directly calling controller methods because this would violate MVC best practices; instead, you should use a delegate.
Delegate Example
To create this scene I’d need a Track Controller that grabs data about the cars from the Model (position, player name, etc), then the controller would send this info to my Car View. The Car View then draws the cars on the screen based on the information it received.
Cool, but now what happens if one car hits the other? I need to handle the collision and adjust the car’s damage. This means checking what the car’s current damage, then adjusting the view to reflect the new info. This requires looking up the database, interpreting the data, and sending updated info to the view.
What you don’t want to do is have the view itself querying the Model, that is the domain of the controller. Essentially the view needs to ask the controller what it needs to do (or in other words, delegate responsibility for handling this event). The view can then get a response from its delegate for how it should handle the situation, it can update itself without ever having ventured outside its own domain.
Creating Custom Delegates
Using the race example above, we would have two files:
- TrackController
A UIViewController that liaises with the Model and sends info to the view for display. - CarView
A UIView which draws a car on the screen based on information it receives, such as damage and position.
What I want to do is set up a delegate so that when this car collides with something, it asks its delegate (in this case, my TrackController) what to do.
Step 1: Add a Protocol to View
First thing I need to do is define what methods my view’s delegate needs to implement. I do this by adding @protocol to the top of my Car.h file:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#import <UIKit/UIKit.h>
@protocol CarDelegate <NSObject>
@required
-(void)handleCollision;
@end
@interface Car : UIView
@property (weak, nonatomic) id <CarDelegate> delegate;
//Other @properties and method declarations here
@end |
The protocol above states that anyone who claims to be a CarDelegate is required to implement the method handleCollision. Note that the Car object does not need to implement the handleCollision method itself.
Also notice the @property “delegate”; the value of this property will be the delegate that implements the defined protocol (more on that below). Don’t forget to @synthesize the delegate property as you would any other.
Step 2: Set Up Your Delegate
Now that my Car view has a protocol, I can set my TrackController as the delegate. This is done in the TrackController.h file:
|
1 2 3 4 5 6 7 8 9 10 |
#import <UIKit/UIKit.h>
#import "Car.h"
@interface TrackController : UIViewController <CarDelegate>
-(void)handleCollision;
@property (nonatomic, strong) Car *aCar;
//Other @properties and method declarations go here
@end |
A few things of note here:
- Car.h has been included at the top of the file
- Added <CarDelegate> to the @interface line. This declares TrackController as conforming to the CarDelegate protocol.
- Declared the handleCollision method defined in the protocol
Make sure you implement the handleCollision method in the .m file of course.
|
1 2 3 |
-(void)handleCollision {
//handle collision here
} |
Step 3: Link Delegate
Everything is set up, but we need to link TrackController to Car before it will start working. Remember the “delegate” @protocol set up in Step 1? This needs to be set as a pointer back to TrackController. The logical place for this is when the Car object has initialised within the TrackController, for example:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#import "TrackController.h"
@implementation TrackController
@synthesize aCar = _aCar;
-(void)handleCollision {
//handle collision here
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.aCar.delegate = self;
}
@end |
Look at the viewDidLoad method above; note that I’m setting the value of the delegate property of “aCar” to self. That is, “aCar” – which is a pointer to Car object – has its delegate method set to TrackController.
You could also set the delegate in aCar’s setter. In fact, that’s probably a better place now that I think about it, but whatever this is just a demo :)
Step 4: Trigger Delegate Method
Now that TrackController is set as my delegate I can trigger the method from Car.m whenever I need to. For example:
|
1 2 3 4 5 |
-(void)checkPositionOfCar {
if(carIsColliding) {
[self.delegate handleCollision];
}
} |
You can also check to make sure the delegate implements the method before calling it like so:
|
1 2 3 |
if([self.delegate respondsToSelector:@selector(handleCollision)]) {
[self.delegate handleCollision];
} |
Done!
Download Example Project
I’ve created a modified version of the red box project from my animation post which uses delegation. You can download the project file here.
In the project, when the user clicks the Move Red Box button the box moves within the white square. Once the animation completes, it sends its new coordinates back to the controller.


