In this case, the idea is that the class that presented the new viewcontroller, should also be the one that dismisses it. The AViewController may have no clue how it was presented, so it wants to let the presenter, the TabBarViewController, handle the dismissal in whatever form needed. So, we need to define a protocol, say AViewControllerProtocol, which allows there to be a standard definition of the dismissal call:
This goes in AViewControllerProtocol.h:
@protocol AViewControllerProtocol <NSObject>
@required
- (void) dismissWithDone:(UIViewController *)viewController;
- (void) dismissWithCancel:(UIViewController *) viewController;
@optional
- (void)dismissWithDone:(UIViewController *)viewController andPlaySoundFile:(NSString *)soundPath;
@end
This goes in a file called AViewControllerProtocol.h. Both TabBarViewController.m and AViewController.m will import it. Think of this as a pact that any user of AViewController must agree to before it can utilize it. Similarly, you can't use UITableView without making a pact to observe the UITableViewDelegate and UITableViewDataSource protocols (those are two separate protocols).
Any class which wants to present AViewController, can see from the protocol definition that AViewController expects two required methods in order to be properly dismissed. It also has an optional third method that requests the presenter to play a sound after dismissing.
In order for AViewController to do its piece of this pact, it needs to get and store one piece of information about who the presenter is. That is called the delegate. The delegate is a property defined in the @interface of AViewController, in AViewController.h:
This goes in AViewController.h:
@property (nonatomic, weak) id<AViewControllerProtocol> delegate;
Now, the presenter, TabBarViewController, needs to do its bit. It needs to define the two required methods, plus maybe the optimal one, and it also needs to set the delegate value:
In TabBarViewController.m, in the @implementation:
This goes in TabBarViewController.m:
- (void) dismissWithDone:(UIViewController *)viewController
{
[self saveData:viewController.dataToSave]; // this could be the results that need to be saved
[viewController dismissViewControllerAnimated:YES completion:^{
;
}];
}
- (void) dismissWithCancel:(UIViewController *)viewController
{
// don't save data
[viewController dismissViewControllerAnimated:YES completion:^{
;
}];
}
The delegate value is set where AViewController is first created and/or before it is presented:
This also goes in TabBarViewController.m:
AViewController * aVC = [AViewController.alloc init];
aVC.delegate = self;
aVC.data = ...; // this may be the data you want changed by the VC
[self presentViewController:aVC animated:YES completion:^{
}];
Setting the delegate here is the only connection that the AViewController class has with its presenting viewController - which is the whole point here: child classes really shouldn't have to know a whole lot about the classes that utilize them.
Lastly, the AViewController class needs to add the following in order to call, via the delegate, back to the presenting class - so in AViewController.m:
This goes in AViewController.m:
-(IBAction)userHitButton:(id)sender
{
if (sender == doneButton) {
if ([_delegate respondsToSelector:@selector(dismissWithDone:)]) {
[_delegate dismissWithDone:self];
}
} else {
if ([_delegate respondsToSelector:@selector(dismissWithCancel:)]) {
[_delegate dismissWithCancel:self];
}
}
}
If you are wondering why the calling class has to:
aVC.delegate = self;
It is because there are situations where the class that actually creates a child class, isn't the one that will handle the delegate calls. In that case, instead of self, you put an instance of a class that will handle the delegate callbacks. For example, lets say that you have AViewController, BViewController and CViewController. They all get data from the user that needs to be saved. A class by the imaginary name of ABCDataHandler could be the one that can handle the dismissWithDone: and dismissWithCancel: callbacks and saves the data as needed, and TabBarViewController can stay out of any data handling activity.
That's to put it as simply as I can. I hope I haven't made any typos here :-)