Skip to content

Instantly share code, notes, and snippets.

@keicoder
Created February 20, 2014 04:47
Show Gist options
  • Save keicoder/9107235 to your computer and use it in GitHub Desktop.
Save keicoder/9107235 to your computer and use it in GitHub Desktop.
objective-c : delegate pattern
//delegate pattern
//delegate pattern is commonly used to handle the following situation
//ex)
//screen A opens screen B and at some point screen B needs to communicate back to screen A, for example when it closes.
//The solution is to make A a delegate of B so screen B can send its messages to A whenever it needs to.
//Screen A launches screen B and becomes its delegate
//The cool thing about the delegate pattern is that screen B doesn’t really know anything about screen A.
//It just knows that some object is its delegate. Other than that, screen B doesn’t care who that is. Just like UITableView doesn’t really care about your view controller, only that it delivers table view cells when the table view asks for them.
//This principle, where screen B is independent of screen A yet can still talk to it, is called loose coupling and is considered good object-oriented design practice.
//At the top of AddItemViewController.h, between the #import and @interface lines:
//ex) inside @protocol ... @end block
@class AddItemViewController;
@class ChecklistItem;
@protocol AddItemViewControllerDelegate <NSObject>
- (void)addItemViewControllerDidCancel:(AddItemViewController *)controller;
- (void)addItemViewController:(AddItemViewController *)controller didFinishAddingItem:(ChecklistItem *)item;
@end
//view controller must have a property that it can use to refer to the delegate
//inside the @interface block, below the other properties:
@property (nonatomic, weak) id <AddItemViewControllerDelegate> delegate;
//In AddItemViewController.m, replace the cancel and done actions with the following
- (IBAction)cancel {
[self.delegate addItemViewControllerDidCancel:self];
}
- (IBAction)done {
ChecklistItem *item = [[ChecklistItem alloc] init];
item.text = self.textField.text;
item.checked = NO;
[self.delegate addItemViewController:self didFinishAddingItem:item];
}
//AddItemViewController.m the compiler doesn’t know yet what a ChecklistItem object is.
//so need to import its definition.
#import "ChecklistItem.h"
//When the user taps the Cancel button, you send the addItemViewControllerDidCancel message to the delegate.
//You do something similar for the Done button, except that the message is addItemViewController:didFinishAddingItem: and you pass along a new ChecklistItem object.
//run the app, the Cancel and Done buttons would no longer appear to work.
//That is because you haven’t told the Add Item screen yet who its delegate is.
//That means the self.delegate property is nil and the messages aren’t being sent to anyone; there is no one listening for them.
//ChecklistsViewController.h
#import "AddItemViewController.h"
@interface ChecklistsViewController : UITableViewController <AddItemViewControllerDelegate>
//#import line for AddItemViewController.h is necessary to load the definition of the AddItemViewControllerDelegate
//ChecklistsViewController.m : implementes protocol’s methods
- (void)addItemViewControllerDidCancel:(AddItemViewController *)controller {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)addItemViewController:(AddItemViewController *)controller didFinishAddingItem:(ChecklistItem *)item {
[self dismissViewControllerAnimated:YES completion:nil];
}
//steps for setting up the delegate pattern between two objects, where object A is the delegate for object B and object B will send out the messages:
1. Define a delegate @protocol for object B.
2. Give object B a property for that delegate protocol.
3. Make object B send messages to its delegate when something interesting happens, such as the user pressing the Cancel or Done buttons, or when it needs a piece of information.
4. Make object A conform to the delegate protocol. It should put the name of the protocol in its @interface line and implement the methods from the protocol.
5. Tell object B that object A is now its delegate.
//This means there is one more thing you need to do:
//tell AddItemViewController that the ChecklistsViewController is now its delegate.
//The proper place to do that when you’re using storyboards is in the prepareForSegue:sender: method.
//ChecklistsViewController.m
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"AddItem"]) {
//1
UINavigationController *navigationController = segue.destinationViewController;
//2
AddItemViewController *controller = (AddItemViewController *)navigationController.topViewController;
//3
controller.delegate = self;
}
}
//prepareForSegue allows you to give data to the new view controller before it will be displayed.
//Usually you’ll do that by setting its properties.
//This is what prepareForSegue does, step-by-step:
1. The new view controller can be found in segue.destinationViewController. In this app, the destination is not AddItemViewController but the navigation controller that embeds it.
2. To get the AddItemViewController object, you can look at the navigation controller’s topViewController property. This property refers to the screen that is currently active inside the navigation controller.
3. Once you have a reference to the AddItemViewController object, you set its delegate property to self and the connection is complete. ChecklistsViewController is now the delegate of AddItemViewController.
//Run the app to see if it works.
//Pressing the + button will perform the segue to the Add Item screen with the Checklists screen set as its delegate.
//When you press Cancel or Done, AddItemViewController sends a message to its delegate,ChecklistsViewController.
//Currently the delegate simply closes the Add Item screen, but you’ll soon make it do more.
//Change the implementation of the didFinishAddingItem delegate method in ChecklistsViewController.m
- (void)addItemViewController:(AddItemViewController *)controller didFinishAddingItem:(ChecklistItem *)item {
NSInteger newRowIndex = [_items count];
[_items addObject:item];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:newRowIndex inSection:0];
NSArray *indexPaths = @[indexPath];
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationAutomatic];
[self dismissViewControllerAnimated:YES completion:nil];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment