I have been using container VCs often recently and I have been wondering what is the best way to communicate between the main Vc and the container VCs. Right now im using notifications, but I would rather use something better. How can I obtain a pointer to the container VCs so that I can at least use delegation? Any better ways?
It is up to the container view controller to set up any necessary connections between itself and the embedded view controller, in its prepareForSegue:sender:
method.
In iOS programming, we have a pattern for this style of communication betwixt view controllers. You can read about it in “Coordinating Efforts Between View Controllers” in the *View Controller Programming Guide for iOS”.
But I think it's easier to understand with a concrete example. Let's use the Google Maps app for iPhone:
I don't know exactly how this app is implemented. But let's suppose there's a top-level AppViewController
that manages the search bar (at the top) and the location bar (at the bottom), and it embeds a MapViewController
in a container view.
There are some interactions between the view controllers. When the user searches, the AppViewController
needs to tell the MapViewController
to place some map markers and zoom in on one of them. When the user taps a map marker, the MapViewController
needs to tell the AppViewController
to display information about that marker in the location bar at the bottom.
So here's the pattern.
First, we define a protocol for the messages that the MapViewController
(which is the embedded view controller) will send to the AppViewController
(which is the container view controller):
@class MapMarker;
@class MapViewController;
@protocol MapViewControllerDelegate <NSObject>
- (void)mapViewController:(MapViewController *)mapViewController didSelectMarker:(MapMarker *)marker;
@end
We will make the AppViewController
conform to this protocol. So the MapViewController
doesn't need to know about the AppViewController
specifically. It just needs a reference to some object that conforms to the protocol. The MapViewController
also needs to understand a message that sets its markers and a message that zooms to a specific marker. So we declare MapViewController
like this:
@interface MapViewController : UIViewController
@property (nonatomic, weak) id<MapViewControllerDelegate> delegate;
- (void)setMarkers:(NSArray *)markers;
- (void)zoomToMarker:(MapMarker *)marker;
@end
Note that the delegate
property is weak
to avoid a retain cycle.
The AppViewController
needs to conform to the MapViewControllerDelegate
protocol. Usually we declare that conformance in a class extension in AppViewController.m
, since conformance doesn't need to be part of AppViewController
's public interface. The AppViewController
also needs a reference to the MapViewController
.
@interface AppViewController () <MapViewControllerDelegate>
@property (nonatomic, strong) MapViewController *mapViewController;
@end
Next, we go into the storyboard, select the embed segue, and give it an identifier:
Now we can implement the prepareForSegue:sender:
method to wire up the properties:
@implementation AppViewController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"MapEmbedding"]) {
[self prepareForMapEmbeddingSegue:segue sender:sender];
}
}
- (void)prepareForMapEmbeddingSegue:(UIStoryboardSegue *)segue sender:(id)sender {
self.mapViewController = segue.destinationViewController;
self.mapViewController.delegate = self;
// We can do any additional setup on mapViewController here,
// like set its initial viewport.
}
Note that AppViewController
also has to implement mapviewController:didSelectMarker:
, and MapViewController
needs to implement setMarkers:
and zoomToMarker:
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With