Advertise Mobile SDKs Books Events Forum News Social Networking Support Us
Follow @iphonedevsdk on Twitter

Interface 2, Advanced iOS
Mockup & Code Gen
($9.99)

Make your own iPhone apps
and run them live!
(free)

Pic Frame Dynamo: Photo Editing
($0.99)

Abiliator
($1.99)

Want your application or service advertised on iPhone Dev SDK?

Go Back   iPhone Dev SDK Forum > iPhone SDK Development Forums > iPhone SDK Tutorials

Reply
 
LinkBack Thread Tools Display Modes
Old 05-26-2011, 09:46 AM   #1 (permalink)
iPhone Development Traine
 
Join Date: Dec 2008
Location: Philadelphia, PA
Posts: 146
MattjDrake is on a distinguished road
Send a message via Skype™ to MattjDrake
Default How and Why to Implement the Delegation Pattern

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.

__________________
Click this to read my blog: How To Make iPhone Apps

Author of Objective-C Recipes, blogger and trainer
MattjDrake is offline   Reply With Quote
Old 03-07-2012, 06:28 AM   #2 (permalink)
Registered Member
 
Join Date: Jun 2009
Posts: 77
tazboy is on a distinguished road
Default

.

Last edited by tazboy; 03-07-2012 at 02:52 PM.
tazboy is offline   Reply With Quote
Old 03-07-2012, 08:31 PM   #3 (permalink)
Just helping out.
 
Domele's Avatar
 
Join Date: Feb 2011
Posts: 2,565
Domele is on a distinguished road
Default

One thing wrong with this tutorial. A delegate property needs to be assign not retain otherwise you'll have a retain cycle and you'll leak.
__________________
If you are looking for a quality developer, I'm your man. Give me a PM if you are interested.

New app - See screenshots and details at www.globaclock.com.

If you want to thank me, click the link. Every click counts. If you want to do more, buy my app. A link is available on my website. Thanks.
Domele is offline   Reply With Quote
Old 05-03-2012, 01:26 PM   #4 (permalink)
New User
 
Join Date: May 2012
Posts: 5
rlove1221 is an unknown quantity at this point
Default

this delegation tutorial explains it better than others I seen , but Im having difficulty in passing along a UIImage. Should I save it and pass it along as a NSString ?
rlove1221 is offline   Reply With Quote
Old 05-03-2012, 01:37 PM   #5 (permalink)
iPhone Development Traine
 
Join Date: Dec 2008
Location: Philadelphia, PA
Posts: 146
MattjDrake is on a distinguished road
Send a message via Skype™ to MattjDrake
Default

Quote:
Originally Posted by Domele View Post
One thing wrong with this tutorial. A delegate property needs to be assign not retain otherwise you'll have a retain cycle and you'll leak.
Domele - thanks for pointing that out. I wrote this before I really understood the retain cycle problem.
__________________
Click this to read my blog: How To Make iPhone Apps

Author of Objective-C Recipes, blogger and trainer
MattjDrake is offline   Reply With Quote
Old 05-03-2012, 01:38 PM   #6 (permalink)
iPhone Development Traine
 
Join Date: Dec 2008
Location: Philadelphia, PA
Posts: 146
MattjDrake is on a distinguished road
Send a message via Skype™ to MattjDrake
Default

Quote:
Originally Posted by rlove1221 View Post
this delegation tutorial explains it better than others I seen , but Im having difficulty in passing along a UIImage. Should I save it and pass it along as a NSString ?
Maybe you should post the code where you're having the problem? My feeling is that you should be able to use an image here.
__________________
Click this to read my blog: How To Make iPhone Apps

Author of Objective-C Recipes, blogger and trainer
MattjDrake is offline   Reply With Quote
Old 05-04-2012, 02:25 AM   #7 (permalink)
New User
 
Join Date: May 2012
Posts: 5
rlove1221 is an unknown quantity at this point
Default

Quote:
Originally Posted by MattjDrake View Post
Maybe you should post the code where you're having the problem? My feeling is that you should be able to use an image here.
thanks for helping !. my mainViewController has 2 UIImageViews image1..image2 and a button . when the button is pressed an actionsheet popsup that gives an option to draw . once selected, drawViewController transitions in . User draws and then once completed I have a button ,the code below, to pass the drawn image to mainViewController. It does save to photoalbum when drawViewController dismisses but nothing populates in image1. I have the property for image1 in mainViewController (strong, nonatomic).

- (IBAction)saveid)sender {
//converts the drawing
UIImage *image = [drawingView imageRepresentation];

[(UIImageView*)mainViewController.image1 setImage:image];
//saves it to library
UIImageWriteToSavedPhotosAlbum(image, self, nil, nil);

[self dismissModalViewControllerAnimated:YES]
rlove1221 is offline   Reply With Quote
Old 05-04-2012, 08:08 AM   #8 (permalink)
iPhone Development Traine
 
Join Date: Dec 2008
Location: Philadelphia, PA
Posts: 146
MattjDrake is on a distinguished road
Send a message via Skype™ to MattjDrake
Default

Ok, how about this: I'm going to assume that we have two view controllers on a storyboard.

The first view has an image view and a button. This view is managed by a class named ViewController. When you press the button a model view pops up with another image view (that you would use to draw) and a button.

When you press the button the model view is dismissed. This model view is managed by a class called drawViewController. When the model view is dismissed the image view on the first screen will display what was created in the model view's image view.

Now, let's implement using delegation. The first thing we need to do is define a protocol for the drawViewController class in drawViewController.h:

Code:
#import <UIKit/UIKit.h>

@protocol drawViewControllerDelegate <NSObject>

@required

-(void)justPickedThisImage:(UIImage *)image;

@end

@interface drawViewControllerViewController : UIViewController

@property (weak, nonatomic) IBOutlet UIImageView *drawImageView;

- (IBAction)pickImage:(id)sender;

@end
The protocol has one required method justPickedThisImage:.

Next, we'll need a delegate property for drawViewController:

Code:
#import <UIKit/UIKit.h>

@protocol drawViewControllerDelegate <NSObject>

@required

-(void)justPickedThisImage:(UIImage *)image;

@end

@interface drawViewControllerViewController : UIViewController

@property (weak) id<drawViewControllerDelegate> delegate;
@property (weak, nonatomic) IBOutlet UIImageView *drawImageView;

- (IBAction)pickImage:(id)sender;

@end
Now we'll need to locate the spot in this model view where the image is picked. Then we'll send a message to the delegate with the image as a parameter before dismissing the model view.

This code is located in the file drawViewController.m.

Code:
#import "drawViewControllerViewController.h"

@implementation drawViewControllerViewController
@synthesize delegate, drawImageView;

- (IBAction)pickImage:(id)sender {
    [self.delegate justPickedThisImage:self.drawImageView.image];
    [self dismissModalViewControllerAnimated:YES];
}

- (void)viewDidUnload {
    [self setDelegate:nil];
    [self setDrawImageView:nil];
    [super viewDidUnload];
}

@end
Note that we also set the delegate to nil in the viewDidUnload method.

Moving On to View Controller Class

ViewController is the view controller class responsible for presenting the model view and then displaying the content that was created. We'll need to adopt the drawViewController protocol first.

This code is in ViewController.h.

Code:
#import <UIKit/UIKit.h>
#import "drawViewControllerViewController.h"

@interface ViewController : UIViewController<drawViewControllerDelegate>

@property (weak, nonatomic) IBOutlet UIImageView *myImageView;

@end
Next, we gotta implement that delegate method in ViewController.m.

Code:
#import "ViewController.h"

@implementation ViewController
@synthesize myImageView;

-(void)justPickedThisImage:(UIImage *)image{
    self.myImageView.image = image;
}

- (void)viewDidUnload{
    [self setMyImageView:nil];
    [super viewDidUnload];
}

@end
This is how we can update the UI. Finally, we need to set the delegate property. Since I'm using Storyboards I'll need to use the prepareForSegue:sender: method (but you could also have this attached to an IBAction that presents the model view).

Code:
#import "ViewController.h"

@implementation ViewController
@synthesize myImageView;

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
    drawViewControllerViewController *dvc = [segue destinationViewController];
    dvc.delegate = self;
}

-(void)justPickedThisImage:(UIImage *)image{
    self.myImageView.image = image;
}

- (void)viewDidUnload{
    [self setMyImageView:nil];
    [super viewDidUnload];
}

@end
That should be it. Let us know how it goes.
__________________
Click this to read my blog: How To Make iPhone Apps

Author of Objective-C Recipes, blogger and trainer
MattjDrake is offline   Reply With Quote
Old 05-04-2012, 01:01 PM   #9 (permalink)
New User
 
Join Date: May 2012
Posts: 5
rlove1221 is an unknown quantity at this point
Default

Quote:
Originally Posted by MattjDrake View Post
Ok, how about this: I'm going to assume that we have two view controllers on a storyboard.

The first view has an image view and a button. This view is managed by a class named ViewController. When you press the button a model view pops up with another image view (that you would use to draw) and a button.

When you press the button the model view is dismissed. This model view is managed by a class called drawViewController. When the model view is dismissed the image view on the first screen will display what was created in the model view's image view.

Now, let's implement using delegation. The first thing we need to do is define a protocol for the drawViewController class in drawViewController.h:

Code:
#import <UIKit/UIKit.h>

@protocol drawViewControllerDelegate <NSObject>

@required

-(void)justPickedThisImage:(UIImage *)image;

@end

@interface drawViewControllerViewController : UIViewController

@property (weak, nonatomic) IBOutlet UIImageView *drawImageView;

- (IBAction)pickImage:(id)sender;

@end
The protocol has one required method justPickedThisImage:.

Next, we'll need a delegate property for drawViewController:

Code:
#import <UIKit/UIKit.h>

@protocol drawViewControllerDelegate <NSObject>

@required

-(void)justPickedThisImage:(UIImage *)image;

@end

@interface drawViewControllerViewController : UIViewController

@property (weak) id<drawViewControllerDelegate> delegate;
@property (weak, nonatomic) IBOutlet UIImageView *drawImageView;

- (IBAction)pickImage:(id)sender;

@end
Now we'll need to locate the spot in this model view where the image is picked. Then we'll send a message to the delegate with the image as a parameter before dismissing the model view.

This code is located in the file drawViewController.m.

Code:
#import "drawViewControllerViewController.h"

@implementation drawViewControllerViewController
@synthesize delegate, drawImageView;

- (IBAction)pickImage:(id)sender {
    [self.delegate justPickedThisImage:self.drawImageView.image];
    [self dismissModalViewControllerAnimated:YES];
}

- (void)viewDidUnload {
    [self setDelegate:nil];
    [self setDrawImageView:nil];
    [super viewDidUnload];
}

@end
Note that we also set the delegate to nil in the viewDidUnload method.

Moving On to View Controller Class

ViewController is the view controller class responsible for presenting the model view and then displaying the content that was created. We'll need to adopt the drawViewController protocol first.

This code is in ViewController.h.

Code:
#import <UIKit/UIKit.h>
#import "drawViewControllerViewController.h"

@interface ViewController : UIViewController<drawViewControllerDelegate>

@property (weak, nonatomic) IBOutlet UIImageView *myImageView;

@end
Next, we gotta implement that delegate method in ViewController.m.

Code:
#import "ViewController.h"

@implementation ViewController
@synthesize myImageView;

-(void)justPickedThisImage:(UIImage *)image{
    self.myImageView.image = image;
}

- (void)viewDidUnload{
    [self setMyImageView:nil];
    [super viewDidUnload];
}

@end
This is how we can update the UI. Finally, we need to set the delegate property. Since I'm using Storyboards I'll need to use the prepareForSegue:sender: method (but you could also have this attached to an IBAction that presents the model view).

Code:
#import "ViewController.h"

@implementation ViewController
@synthesize myImageView;

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
    drawViewControllerViewController *dvc = [segue destinationViewController];
    dvc.delegate = self;
}

-(void)justPickedThisImage:(UIImage *)image{
    self.myImageView.image = image;
}

- (void)viewDidUnload{
    [self setMyImageView:nil];
    [super viewDidUnload];
}

@end
That should be it. Let us know how it goes.
Thank you again for you help! I started my project as a 'SingleView" so Im not sure if I can add the Segue code/ StoryBoard in it . should I just redo the project as a storyboard if thats the only way it can be done?
rlove1221 is offline   Reply With Quote
Old 05-04-2012, 01:04 PM   #10 (permalink)
iPhone Development Traine
 
Join Date: Dec 2008
Location: Philadelphia, PA
Posts: 146
MattjDrake is on a distinguished road
Send a message via Skype™ to MattjDrake
Default

Quote:
Originally Posted by rlove1221 View Post
Thank you again for you help! I started my project as a 'SingleView" so Im not sure if I can add the Segue code/ StoryBoard in it . should I just redo the project as a storyboard if thats the only way it can be done?
You can use this pattern with or without Storyboards. The key thing is how the delegate method was implemented. If you really want to copy this exactly how I did it, then yes you'll need to use a storyboard based app or just add a storyboard to your application.
__________________
Click this to read my blog: How To Make iPhone Apps

Author of Objective-C Recipes, blogger and trainer
MattjDrake is offline   Reply With Quote
Old 05-04-2012, 06:03 PM   #11 (permalink)
New User
 
Join Date: May 2012
Posts: 5
rlove1221 is an unknown quantity at this point
Default

Quote:
Originally Posted by MattjDrake View Post
You can use this pattern with or without Storyboards. The key thing is how the delegate method was implemented. If you really want to copy this exactly how I did it, then yes you'll need to use a storyboard based app or just add a storyboard to your application.
gotcha, Im going to figure it out without Storyboards first.
rlove1221 is offline   Reply With Quote
Old 05-22-2012, 05:27 PM   #12 (permalink)
New User
 
Join Date: May 2012
Posts: 2
Shukaku is an unknown quantity at this point
Default

Excellent tutorial MattJDrake. This was one tutorial that could get me to to really want to do this!

Now, I'm facing this problem where I don't know how to apply the last step.

I am creating an example application where there's view1 class and a popupview class.

There is a button and a label on the first class' view. The button calls the [self.view addsSubViewopupviewObj.view];

On the popupview which has then appeared, there's a textfield and a 'send' button. Now I want that when the user click the sendbutton the [self.view removeFromSuperview]; method gets called and the application returns to view1 class but with the text in the label changed to what I entered in the popup class.

Can you guide me what different do I need to do in this application from what you did? I am using XIBs.

Thanks in advance.
Shukaku is offline   Reply With Quote
Old 05-22-2012, 06:14 PM   #13 (permalink)
New User
 
Join Date: May 2012
Posts: 2
Shukaku is an unknown quantity at this point
Default

Also, I would like to know where to use the delegation pattern other than just to pass the variables because from what I know, it can be accomplished with forwarding of variables (in the viewDidUnload function of the popup for example) .

The real answer that I am looking for is where the use of custom delegate is absolutely necessary. An example will be really helpful.

Last, please don't mind if my questions come across as very novice-grade. I am new not only to the iPhone Programming, but to the programming scene altogether!

Thanks!

Last edited by Shukaku; 05-22-2012 at 06:18 PM.
Shukaku is offline   Reply With Quote
Reply

Bookmarks

Tags
delegation, tutorial, uitableview

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is On
Trackbacks are On
Pingbacks are On
Refbacks are On



» Advertisements
» Online Users: 478
14 members and 464 guests
alexeir, David-T, Dj_kades, foslock, iAppDeveloper, jeroenkeij, LunarMoon, Mijator, Pauluz85, pipposanta, QuantumDoja, robsmy, sacha1996, usernametaken
Most users ever online was 1,387, 04-10-2012 at 04:21 AM.
» Stats
Members: 175,679
Threads: 94,129
Posts: 402,928
Top Poster: BrianSlick (7,990)
Welcome to our newest member, xzoonxoom
Powered by vBadvanced CMPS v3.1.0

All times are GMT -5. The time now is 09:24 AM.
Powered by vBulletin® Version 3.8.0
Copyright ©2000 - 2012, Jelsoft Enterprises Ltd.
Search Engine Friendly URLs by vBSEO 3.3.0