The first time I wrote code to present a modal view controller in iOS, I naively assumed that the final argument to the
presentViewController:animated:completion: method was a callback that would be invoked after the user had finished interacting with the presented controller's view (i.e. when the controller was dismissed). I was surprised to find my completion block being called as soon as the controller's view appeared on screen, rather than when it when it disappeared. Of course, after reading the documentation more carefully, I realized that this was the intended behavior:
completion- The block to execute after the presentation finishes.
Unfortunately, that's not what I needed. After looking in vain for a means to provide the controller with a callback to invoke on dismissal, I finally came across this information in Apple's View Controller Programming Guide for iOS:
If the presented view controller must return data to the presenting view controller, use the delegation design pattern to facilitate the transfer...With delegation, the presented view controller stores a reference to a delegate object that implements methods from a formal protocol. As it gathers results, the presented view controller calls those methods on its delegate. In a typical implementation, the presenting view controller makes itself the delegate of its presented view controller.
That was all the document had to say - no code samples or other, more specific, recommendations were provided. Fortunately, after some digging, I was able to identify a couple existing UIKit classes that I could use as a model.
UIImagePickerController, which allows a user to select a photo or video to use in an application, defines a
UIImagePickerControllerDelegate protocol that is used to notify the presenter when the user either makes a selection or cancels the operation:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person - (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker ...
ABPeoplePickerNavigationController, along with its associated protocol, is deprecated in iOS 9, but its replacement,
CNContactPickerViewController, takes a similar approach with
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact - (void)contactPickerDidCancel:(CNContactPickerViewController *)picker ...
With these classes as a guide, I was able to easily define and implement my own custom controller delegate, which notifies a presenting controller that a user registration controller has either successfully registered a new user or has been cancelled:
// Forward reference @protocol RegistrationViewControllerDelegate; /** * Registration view controller. */ @interface RegistrationViewController : UITableViewController @property (weak, nonatomic) id<RegistrationViewControllerDelegate> delegate; @end /** * Registration view controller delegate. */ @protocol RegistrationViewControllerDelegate - (void)registrationViewController:(RegistrationViewController *)registrationViewController didRegisterUser:(User *)user; - (void)registrationViewControllerDidCancel:(RegistrationViewController *)registrationViewController; @end