Many of the UIKit classes in iOS SDK use the Delegation design pattern. Delegation means that you designate one object to act on behalf of another object. A common example of this is the table view in UIKit. A table view will require an object to act as a delegate to do things like return the number of rows in a section, return a table cell and so on.
You are probably already familiar with using Delegation as its presented to you in UIKit. Today I want to show you how to implement delegation for yourself, but first let's talk about why you would want to do this.
Use Case
Have you ever had views in a navigation controller that have a parent-child type relationship where you can edit data in the child view that is supposed to be updated in the parent view? For example, in my app users can select a note from a table view and get directed to a child screen that allows them to edit the note.
The problem is that the original note screen does not get updated with the new information when the user touches the back button (at least not without reloading the entire table view which could get expensive).
How Delegation Can Help
Delegation is a nice way to handle this situation. What I do is have the parent table view controller act as a delegate for the child editing view. When a user is done writing a note the editing view controller will send a message back to the parent table view controller. This gives me a chance to update the UI with the new note information.
What You Need To Use Delegation
Let's assume that you have a navigation based app with a table view (let's call this ParentTable) that holds a list of strings. When you touch a cell on the table view you are taken to a view (let's call this ChildEditor) where you can edit the string.
You are going to need to do these things to use Delegation:
1.) Define a protocol for ChildEditor
2.) Add a delegate property to ChildEditor that requires the protocol from step 1
3.) Add a NSIndexPath property to ChildEditor to remember what table view cell the string is displayed in
4.) When ChildEditor updates a string it must send a message to the delegate property from step 2
5.) ParentTable must adopt the protocol from the step 1
6.) ParentTable must implement the delegate method that corresponds to the message that will be sent when ChildEditor updates a string (this is when the UI is updated)
7.) Before a new ChildEditor is pushed onto the navigation controller set the delegate property to the ParentTable (you can use the self keyword here)
8.) Before a new ChildEditor is pushed onto the navigation controller set the indexPath property to the property that you get in the didSelectRowAtIndexPath method
Example Code
To test this out for yourself you can create a navigation based app with XCode. You will need to add your own ChildEditor that has a text field hooked up with Interface Builder already. To stay consistent, I named the root table view ParentTable (you can use XCode's refactoring tool to do this).
Let's go through all the steps above now but adding in the code. To make sure that you can see the code in context I include all the code in the file and then bold the code that was added in each step - sometimes you may need to scroll down to see the new code.
Quick note here: textToEdit is the string that we will editing here and updating the ParentTable UI with.
1.) Define a protocol for ChildEditor
ChildEditor Header File (ChildEditor.h)
#import <UIKit/UIKit.h>
@protocol ChildEditorDelegate <NSObject>
-(void) thisNoteWasJustUpdated: (NSMutableString *)note atThisIndexPath: (NSIndexPath *)noteIndexPath;
@end
@interface ChildEditor : UIViewController<UITextFieldDelegate> {
NSMutableString *textToEdit;
UITextField *editingTextField;
}
@property(nonatomic, retain) NSMutableString *textToEdit;
@property(nonatomic, retain) IBOutlet UITextField *editingTextField;
@end
2.) Add a delegate property to ChildEditor that requires the protocol from step 1
AND
3.) Add a NSIndexPath property to ChildEditor to remember what table view cell the string is displayed in
ChildEditor Header File (ChildEditor.h)
#import <UIKit/UIKit.h>
@protocol ChildEditorDelegate <NSObject>
-(void) thisNoteWasJustUpdated: (NSMutableString *)note atThisIndexPath: (NSIndexPath *)noteIndexPath;
@end
@interface ChildEditor : UIViewController<UITextFieldDelegate> {
NSMutableString *textToEdit;
UITextField *editingTextField;
NSIndexPath *indexPath;
id<ChildEditorDelegate> delegate;
}
@property(nonatomic, retain) NSMutableString *textToEdit;
@property(nonatomic, retain) IBOutlet UITextField *editingTextField;
@property(nonatomic, assign) NSIndexPath *indexPath;
@property(nonatomic, retain) id<ChildEditorDelegate> delegate;
@end
ChildEditor Implementation File(ChildEditor.m)
#import "ChildEditor.h"
@implementation ChildEditor
@synthesize textToEdit, editingTextField, indexPath, delegate;
...
[CODE OMITTED]
...
-(void)dealloc{
[delegate release];
delegate = nil;
[indexPath release];
[editingTextField release];
[textToEdit release];
[super dealloc];
}
@end
4.) When ChildEditor updates a string it must send a message to the delegate property from step 2
ChildEditor Implementation File(ChildEditor.m)
#import "ChildEditor.h"
@implementation ChildEditor
@synthesize textToEdit, editingTextField, indexPath, delegate;
- (BOOL)textFieldShouldReturn: (UITextField *)textField{
[textToEdit appendString:textField.text];
[delegate thisNoteWasJustUpdated:textToEdit atThisIndexPath:indexPath];
[textField resignFirstResponder];
[self.navigationController popViewControllerAnimated:YES];
return YES;
}
...
[CODE OMITTED]
...
@end
5.) ParentTable must adopt the protocol from the step 1
ParentTable Header File(ParentTable.h)
#import <UIKit/UIKit.h>
#import "ChildEditor.h"
@interface ParentTable : UITableViewController<ChildEditorDelegate>{
NSMutableArray *listOfStrings;
}
@property(nonatomic, retain) NSMutableArray *listOfStrings;
@end
6.) ParentTable must implement the delegate method that corresponds to the message that will be sent when ChildEditor updates a string (this is when the UI is updated)
ParentTable Implementation File(ParentTable.m)
#import "ParentTable.h"
@implementation ParentTable
@synthesize listOfStrings;
-(void) thisNoteWasJustUpdated: (NSMutableString *)note atThisIndexPath: (NSIndexPath *)noteIndexPath{
UITableViewCell *tvc = [self.tableView cellForRowAtIndexPath:noteIndexPath];
tvc.textLabel.text = note;
}
- (void)dealloc{[listOfStrings release];
[super dealloc];
}
...
[CODE OMITTED]
...
@end
NOTE: here listOfStrings is an array that is serving as our makeshift data model. The other code is typical table view controller delegate methods that you need to implement to make table views work.
7.) Before a new ChildEditor is pushed onto the navigation controller set the delegate property to the ParentTable (you can use the self keyword here)
AND
8.) Before a new ChildEditor is pushed onto the navigation controller set the indexPath property to the property that you get in the didSelectRowAtIndexPath method
ParentTable Implementation File(ParentTable.m)
#import "ParentTable.h"
@implementation ParentTable
@synthesize listOfStrings;
-(void) thisNoteWasJustUpdated: (NSMutableString *)note atThisIndexPath: (NSIndexPath *)noteIndexPath{
UITableViewCell *tvc = [self.tableView cellForRowAtIndexPath:noteIndexPath];
tvc.textLabel.text = note;
}
...
[CODE OMITTED]
...
- (void)tableView: (UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath{
ChildEditor *detailViewController = [[ChildEditor alloc] initWithNibName:@"EditorView" bundle:nil];
detailViewController.textToEdit =[listOfStrings objectAtIndex:indexPath.row];
detailViewController.indexPath = indexPath;
detailViewController.delegate = self;
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
}
@end
Final Thoughts
You should be able to run your app, edit your note and then see the changes when you return back to the starting screen.
This example has very simple on purpose, but you can probably imagine lots of situations where you may want to implement Delegation in your own app. I have found that this method works pretty well in data model objects when information may be altered from the app or other sources like web services.