I have a complex iPad view that I manage by having several view controllers. I previously (before iOS6/XCode 4.5) did this by allocating my view controllers in code, and hooked
I'm not sure what you mean by "retrieve the embedded controller". When you want to use a controller you use the UIStoryboard method instantiateViewControllerWithIdentifier:, using the identifier that you give to the controller in IB. You can also use the performSegueWithIdentifier:sender: method (which also instantiated the view controller). You should check out the "Using View Controllers in Your App" section in the Apple docs. It also makes reference to the fact that child view controllers are instantiated at the same time as the container controller.
After edit: If you embed a container view in another view controller, that embedded view's controller can be referenced from the containing controller with self.childViewControllers (which will be an array, so if there is just one, you can get it with lastObject).
Another option for some cases is to capture the embedded controller using -prepareForSegue:sender:
.
For example, if I have a UINavigationController
embedded within a CustomContainerViewController
, I can name the embed segue embedContentStack
in the storyboard and capture it in CustomContainerViewController
via
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"embedContentStack"]) {
// can't assign the view controller from an embed segue via the storyboard, so capture here
_contentStack = (UINavigationController *)segue.destinationViewController;
}
}
Here is another thread about it: Access Container View Controller from Parent iOS
They propose to keep a reference in prepareForSegue or search for the embedded viewController in self.childViewControllers
Before proceeding to use an answer to this question, you may wish to reflect whether the embedded things really need to be view controllers.
Eg, if you're embedding a UICollectionViewController
subclass, could you instead embed a UICollectionView
subclass? Or, even better, could you embed a UIView
subclass that hides away the UICollectionView
behind a simple ViewModel?
In the code base I'm currently working on, I'm embedding two view controllers in to another view controller. Both could fairly easily be plain views instead, and could then be more easily bound to in the storyboard, without this messy code.
Unfortunately, they are currently view controllers and I'm not in a position to simplify them in to plain views right now, so this will have to do.
I'm using the approach of picking up the embed segue in prepare(for segue:, sender:)
as suggested by Playful Geek here.
I wanted to show the swift I'm using for this, as it seems to be fairly tidy…
class EditionLandingViewController: UIViewController {
fileprivate var titlesView: SectionTitlesViewController!
fileprivate var sectionsView: SectionsViewController!
}
//MARK:-
extension EditionLandingViewController {
private enum SegueId: String {
case embedTitles
case embedSections
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
guard
let segueRawId = segue.identifier,
let segueId = SegueId(rawValue: segueRawId)
else { return }
switch segueId {
case .embedTitles:
self.titlesView = segue.destination as! SectionTitlesViewController
case .embedSections:
self.sectionsView = segue.destination as! SectionsViewController
}
}
}
I've chosen to name segues as action methods.
Using an enum
cases for segue identifiers means you've got the compiler and tooling on your side, so its much harder to get a segue name wrong.
Keeping the segue ids in a private enum
within the extension
scope seems appropriate in this case as these segues are not needed anywhere else (they can't be perform
ed, for example).
I'm using implicitly unwrapped types for the embedded view controllers because (in my case anyway) it's a logic error if they are missing.
Similarly, I'm also happy to force cast the destination view controller types. Again, it would be a logic error if these types are not the same.