Designing a Protocol for the Group Edit View Controller

In my last post, I decided to remove my Singleton object in preference for passing the NSManagedObjectContext around between view controllers.  In my application, I have a table view that shows a list of ‘groups’, and a view that provides the form for entering or altering the group ‘title’.

GroupTableViewController - non-editing showing the edit button in the navigation bar, and the Add button in the toolbar

GroupTableViewController - Once editing is touched, the edit controls appear to allow for deletion, modification and reordering

GroupEditViewController - This view is shown when adding a new group or editing the title of an existing group


The intention is to allow the user to both add new groups, and edit the titles of the existing groups.  The same view can be used for both tasks, if setup correctly.

This raised some issues about how to efficiently code:

  • Instantiation of the GroupEditViewController, providing the necessary inputs
  • Deciding who should be responsible for saving changes to the objects
  • Notifying the initiator that the Group Edit was complete.

Definition of the Protocol

GroupEditViewController will be using a protocol that it’s delegate will implement, and we will define this as GroupEditViewControllerDelegate and place it in GroupEditViewController.h.

@protocol GroupEditViewControllerDelegate
    -(void) groupEdit: (GroupEditViewController *) groupEdit didCompleteGroupEdit: (BOOL) didSave;
@end

We need to add properties to the class to store the instance variables passed in:

@interface GroupEditViewController : UIViewController <UITextFieldDelegate> {
    UITextField *groupTitle;
    Group *group;
    BOOL editing;
    id  delegate;
}

@property (nonatomic,retain) IBOutlet UITextField *groupTitle;
@property (nonatomic,retain) Group *group;
@property (nonatomic,assign,getter=isEditing) BOOL editing;
@property (nonatomic,assign) id &lt;GroupEditViewControllerDelegate&gt; delegate;

-(GroupEditViewController *) initWithGroup: (Group *) group forDelegate: (id <GroupEditViewControllerDelegate>) delegate isEditing: (BOOL) isEditing;
-(IBAction) didCancelGroupEdit;
-(IBAction) didCompleteGroupEdit;

@end

GroupTableViewController needs to specify that it implements the protocol in its @implementation statement:

@interface GroupTableViewController : UIViewController
    <   UITableViewDelegate,
        UITableViewDataSource,
        NSFetchedResultsControllerDelegate,
        GroupEditViewControllerDelegate
    > {
...

Instantiation of the GroupEditViewController

I wanted a simple way of creating the GroupEditViewController, and decided that the class would benefit from a default initialiser that took the relevant inputs.  Without the default initialiser, we would normally do

GroupEditViewController *groupEdit = [[GroupEditViewController alloc] init];

We need to provide:

  • The group object to be edited (or created)
  • The delegate for receiving notifications
  • An indicator if the Group Edit view is editing an existing object




The following code is placed in GroupTableViewController.m, in my case this is in the accessoryButtonTappedForRowWithIndexPath method.

- (void) tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
    Group *group = [self.fetchedResultsController objectAtIndexPath:indexPath];
    GroupEditViewController *groupEdit = [[GroupEditViewController alloc] initWithGroup:group forDelegate:self isEditing:YES];

    UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:groupEdit];
    [self presentModalViewController:navigationController animated:YES];
    [navigationController release];
    [groupEdit release];
}

Note that in this instance we are editing an existing group object, which is retrieved from the fetchedResultsController.  We also need a method to deal with the Add Group action when touching the toolbar button:

- (IBAction) didTouchAddGroupButton {
    Group *group = [NSEntityDescription insertNewObjectForEntityForName:@"Group" inManagedObjectContext:self.managedObjectContext];
    GroupEditViewController *groupEdit = [[GroupEditViewController alloc] initWithGroup:group forDelegate:self isEditing:NO];
    UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:groupEdit];
    [self presentModalViewController:navigationController animated:YES];
    [navigationController release];
    [groupEdit release];
}

Note that this time, we create a new Group object to pass to the GroupEditViewController.

We need to add the implementation of the default initializer to GroupEditViewController.m.

-(GroupEditViewController *) initWithGroup: (Group *) withGroup forDelegate: (id <GroupEditViewControllerDelegate>) viewDelegate isEditing: (BOOL) isEditing {
    self = [super init];
    if (self) {
        self.group = withGroup;
        self.delegate = viewDelegate;
        self.editing = isEditing;
    }
    return self;
}

Deciding who should be responsible for saving changes

I decided that it was easier for the invoker, GroupTableViewController, to be responsible for the creation of new objects.  When adding a new group, GroupTableViewController needs to create the new Group object, which may in turn be deleted by GroupEditViewController in the event of cancelling the edit.

Notifying the initiator that the Group Edit was complete

Within GroupEditViewController.m we need to setup the view so that the navigation controller that it is placed within has the appropriate cancel and save buttons, and we’ll programmatically connect their actions to handling methods.

-(void) viewDidLoad {
    [super viewDidLoad];

    self.navigationItem.title = self.isEditing ? @"Edit Group" : @"Add Group";

    UIBarButtonItem *cancelButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Cancel" style:UIBarButtonItemStyleBordered target:self action:@selector(didCancelGroupEdit)];
    self.navigationItem.leftBarButtonItem = cancelButtonItem;
    [cancelButtonItem release];

    UIBarButtonItem *saveButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Save" style:UIBarButtonItemStyleDone target:self action:@selector(didCompleteGroupEdit)];
    self.navigationItem.rightBarButtonItem = saveButtonItem;
    [saveButtonItem release];

    self.groupTitle.text = self.group.title;

    [self.groupTitle becomeFirstResponder];
}

We add methods to be the targets for the actions of the navigation buttons.

-(IBAction) didCancelGroupEdit {
    if (self.isEditing == NO) {
        [self.group.managedObjectContext deleteObject:self.group];
        [self save];
    }
    [self.delegate groupEdit:self didCompleteGroupEdit: NO];
}

-(IBAction) didCompleteGroupEdit {
    self.group.title = self.groupTitle.text;
    [self save];
    [self.delegate groupEdit:self didCompleteGroupEdit: YES];
}

And finally, in GroupTableViewController.m, we add the delegate to receive the notification that the edit was completed.

-(void) groupEdit:(GroupEditViewController *)groupEdit didCompleteGroupEdit:(BOOL)didSave {
    [self dismissModalViewControllerAnimated:YES];
}




Leave a Reply

Your email address will not be published. Required fields are marked *