30

I have a Storyboard setup with a UIViewController with an container view so that I can embed another UIViewController inside of it.

In a certain scenario I need to change the embedded view controller. In my storyboard I cannot have two segues from my container view (only a single embed segue). Which leads me to doing it programatically.

I have my container view in my storyboard with no connected embed segue.

Now from this point, how can I programmatically embed my chosen UIViewController object?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Josh Kahane
  • 16,765
  • 45
  • 140
  • 253

2 Answers2

36

You can do this by programmatically, below is the method which will take a bool value to make decision which view controller need to be added in container view and then will instantiate an object and after that will add it to containerView

- (void)addViewControllerToContainerView:(BOOL)addVC1
{
// Get storyboard
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:@"<name of storyboard>" bundle:[NSBundle mainBundle]];
    UIViewController *viewController = nil;
    if (addVC1)
    {
// get viewController with identifier 
        viewController = [storyBoard instantiateViewControllerWithIdentifier:@"<View Controller 1 Identifier>"];
    }
    else
    {
        viewController = [storyBoard instantiateViewControllerWithIdentifier:@"<View Controller 2 Identifier>"];
    }
// lets add it to container view
    [viewController willMoveToParentViewController:self];
    [self.view addSubview:viewController.view];
    [self addChildViewController:viewController];
    [viewController didMoveToParentViewController:self];
// keep reference of viewController which may be useful when you need to remove it from container view, lets consider you have a property name as containerViewController
    self.containerViewController = viewController;
}

When you need remove view controller from container view controller you can do this

   [self.containerViewController willMoveToParentViewController:nil];  // 1   
   self.containerViewController.view removeFromSuperView];
   [self.containerViewController removeFromParentViewController];//this line is updated as view is removed from parent view cotnroller istead of its viewcontroller is removed from parentViewController 
   self.containerViewController = nil

Apple docs about container view controllers

Er. Khatri
  • 1,384
  • 11
  • 29
Adnan Aftab
  • 14,377
  • 4
  • 45
  • 54
  • Thanks for the comprehensive answer. Unfortunately it doesn't quite work as I imagined. Firstly, I need to enabled a `UIViewController` inside a `UIView` of the root `UIViewController`. This fills the whole view. Secondly, if I add it as a subview of the `UIView` (instead of the entire VC view), it doesn't conform to auto layout constraints like it would embedded from the storyboard. – Josh Kahane Aug 23 '15 at 13:37
  • This thing is achievable this approach, and that's why there is concept of child and parent view controller, when you will add one view controller as child view controller you will get all other lifecycle methods called in child vc, and surely AutoLayoutWill work as well. – Adnan Aftab Aug 23 '15 at 13:40
  • I have add link to apple docs which describes how to implement container view controllers – Adnan Aftab Aug 23 '15 at 13:41
  • Thanks for your help. With simple alteration to your suggested code, I got it working perfectly. Simply changing the view I add the child view controller to and add constraints to that view worked just fine. Thanks again. – Josh Kahane Aug 23 '15 at 15:09
  • 6
    You should not be calling willMoveToParentViewController before addChildViewController because the system calls this for you. From https://developer.apple.com/reference/uikit/uiviewcontroller/1621381-willmovetoparentviewcontroller: "When your custom container calls the addChildViewController: method, it automatically calls the willMoveToParentViewController: method of the view controller to be added as a child before adding it." – JimmyB Jan 03 '17 at 08:10
0

Swift version with UIPageViewController (which I believe is a common use case for this)

let pageController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
// do your PageViewController stuff here (delegate and dataSource)
pageController.willMove(toParent: self)
containerView.addSubview(pageController.view)
pageController.view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
            pageController.view.topAnchor.constraint(equalTo: containerView.topAnchor),
            pageController.view.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
            pageController.view.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
            pageController.view.trailingAnchor.constraint(equalTo: containerView.trailingAnchor)
])
addChild(pageController)
pageController.didMove(toParent: self)

I didn't figure out how to make use of the UIPageControll included in UIPageViewController so I ended up using my own instance. But I believe this is out of the scope of this question.