Adding an iOS Container View in Xcode

If you are new to iOS programming, you may not know of a very useful feature in iOS called container views. A container view allows you to create a reusable component that all of your view controllers can share. The containers are easily edited with the storyboard editor of Xcode.  Plus the reusable component is also backed by the power of a separate child view controller.

Before container views were introduced, we had to create reusable views using UIViews stored in xibs. You could not see them in the storyboard next to our view controllers. Then we had to programmatically add them to our view controller when the view loaded. It was a real pain. Now you can add a reusable container view without writing any code.

The example app I built has just two view controllers, and I will create a container view that is shared between them.

The first thing to do is find the container view in the object library of Xcode and drag it onto my first view controller. I find it easiest to filter the object library for the thing I am looking for.

PickContainerView

 

AddContainerView2

After dragging the container view, constrain it to have the size and position you want in your view controller. You will see that the child view controller changes size automatically when you constrain the container view in the view controller. I added a blue background and a label called “My Component” to the child view controller of the container so you can easily see it. You will also see a special segue, called an embed segue, is added that links the container view to the child view controller of your component.

Now let’s reuse our component in another view. To do this add another container view in the second view controller. The second container view should have the same size and position of the first one, so that your users will not get confused by the component moving around when you transition. However it is not required that you do so.  In fact for demonstration purposes, I will add my second container view to another area of my second view controller.

AddingSecondContainerView

We will not need the child view controller that was automatically added when you added the container view. Select it and delete it so that you are only left with an empty container view that is not linked to a child view controller. The next step is to link the container view to our child view controller that we already created before. Hold the Ctrl key and drag with the mouse from the container view to the child view controller. Now add an embed segue.

AddEmbedSeque2

That should do it. You now have a child view controller that is shared among two view controllers. You can add this child view controller to as many view controllers as you want. Here is what the finished app looks like.

FirstContainerView


Catch up on my other posts in this series:


For more on using Xcode, read my series on unwind segues:

Conversation
  • Adam Johns says:

    A couple of questions about doing this in practice:

    1. Doesn’t accessing the elements of your container inside your ViewController become much more of a pain? You can’t simply ctrl-drag outlets from the container to the ViewController that contains them.

    2. What if you have 20 ViewControllers using the same container? Do you have a spider web of embed segues in your storyboard? Or do you somehow put the container into its own storyboard and use the new storyboard reference functionality in Xcode 7?

    • Mike Woelmer Mike Woelmer says:

      Hi Adam,

      Here are the answers to your questions:

      1. The view that is inside your container has its own view controller therefore you can drag and drop outlets easily. Just click on the container view controller and set a custom class.

      This trick is great for more purposes other than reusing views. I usually use containers to break up complicated view controllers into smaller manageable view controllers even if I am not going to reuse them.

      2. Yes you would have a spider web of embed segues. I haven’t tried putting a container view controller in a separate storyboard. That would be interesting if that did work however you will still need segues to link to the other storyboard, so I am not sure that helps with your spider web concern.

      • Adam Johns says:

        I was thinking maybe every time you referenced the container storyboard you would copy the storyboard reference in IB, and segue to a different storyboard reference (even though it would still contain the same thing inside). As opposed to just copying your container view everywhere which would require you to update all of them if you changed something.

  • Gary says:

    Hi Adam,

    Is the container static within the parent controller or can it display and disappear like a modal?

  • Priyanka Napal says:

    Hi, i read your blog segment control with container view, i am working on the same , Can you please little explain how to access the controllers associated with that container view fro segment control.

    • Mike Woelmer Mike Woelmer says:

      Hi Priyanka Napal,

      If you need a reference to the child view controllers, here is a useful trick. It turns out that when you embed a child view controller, the UIViewController’s -> prepareForSegue function is called when the embed happens. At that point you can save a reference to the child view controller in your parent view controller. Here are the steps:

      1. Override prepareForSegue function in your parent view controller.
      2. Click on the embed segue that ties the container view to your child view controller and give it an identifier in Interface Builder. I named mine “myEmbeddedSegue”.
      3. Add the following code to your prepareForSegue:

      
      override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
      {
          if (segue.identifier == "myEmbeddedSegue") {
              let childViewController = segue.destinationViewController as! myChildViewController
              // Now you have a pointer to the child view controller. 
              // You can save it, or call public functions on it
          }
      }
      
      • suny says:

        For accessing ChildViewControllers can you not simply do “self.childViewControllers” and it can return you an array of child controllers.
        (maybe you are trying to do something else :)

        • Mav says:

          You can’t do that because the controllers you are trying to get a handle to are not children of your view controller. They are in a separate view linked by the embed segue. Mike’s comment works and really is useful.

  • jasiek1333 says:

    Nice tutorial! One question though. You say “Now add an embed segue” but which segue is the embed segue? Which segue from the list on the screenshot is the embed segue ?

    • Mike Woelmer Mike Woelmer says:

      It is the 5th one down on the list called “embed”. See the screen shot of the popup above.

  • matt2277 says:

    Thanks for the tutorial. I have a question: Let’s say the parent view controller is NOT the initial VC – how do I pass variables to the childContainer?

    For my app, I have a VC1 (the initial controller), and when I click a button, it segues to a 2nd view controller (VC2), which uses a tableViewController to display a variable from VC1. This VC2child needs the data from VC1 and I don’t know the best way to do that.

    I’ve scoured the internet for nearly 24hrs and I haven’t found the right way to do this. I know how to segue and pass data from VC1 to VC2, but I need one step further (getting the data to VC2child). Do you have any idea how to do this???

    • Mike Woelmer Mike Woelmer says:

      Hi Matt,

      In your VC2, override the UIViewController function called prepareForSegue. This function will get called before you transition from VC2 to another top level view controller (like VC3). It is also called when your VC2 is loaded and your VC2 contains a child view controller. One of the parameters to the function is a UIStoryboardSegue which you can use to get access to the “destinationViewController” (VC2child) and “sourceViewController” (VC2) properties. You will have to cast these as your subclass of UIViewController to access the properties that you want to set. Make sure you set an identifier in interface builder on your embed segue, and check the segue.identifier parameter to the function to be sure you are reacting to the correct segue.

      Hope that helps.
      –Mike.

  • Wilson says:

    Nice tutorial! Is there a way that different VCs can share the same instance of the container? In my case I am working on an app that the container view will keep updating the data while transiting to different view.

    • Mike Woelmer Mike Woelmer says:

      Hi Wilson, If your container is just a view of common data/state that is shared between your view controllers, then I would allow there to be two instances of the controller. Each container would render the view based on the current data/state that is stored in Core Data or some other shared object.

  • Marc says:

    When I add a view container with an embedded UIViewController to a UIStackView, I get warnings about missing Y position or height constraints for all of the views in the UIStackView. Is it possible for the view container to use the intrinsic size of the view in the view container?

    • Mike Woelmer Mike Woelmer says:

      Hi Marc,

      Great question. StackViews tell their children how big they should be — not the other way around. I think you are wanting the StackView to obey the intrinsic content size of the container view and that is not possible. Otherwise settings on the StackView like “Fill Equally” would not work if the StackView had to obey the size requests from their children.

  • Lahiru Pinto says:

    Container view, in default it provide segue link to “View Controller” how do we change it to “Table view controller”

    • Mike Woelmer Mike Woelmer says:

      Hi Lahiru,

      You can change the default class to be a UITableViewController. Click on your child view controller. Click on the identity inspector tab in the Utilities pane on the right. Now change the class to be UITableViewController or a custom class of yours that derives from UITableViewController.

  • Anu says:

    Hi Mike,
    I have used container views in my project. In one of the container viewI have embedded a tableviewcontroller. Everything works fine as expected. However when I run the app on device, the tableviewcontroller page does not actually fit properly to the screen. It is displaced from the top of the page and some rows gets hidden at the bottom. I have tried with constraints and all. Any workaround???

  • Guillaume says:

    Hello Mike,
    I’m using container views inside my project to display a UIPageViewController, however I have issue with auto-layout when my pageviewcontroller load its child controller the constraints doesn’t apply on the elements inside my child (as you can see here : http://imgur.com/vx1R9BB, my label is supposed to be centered inside my view) , do you have any idea for this issue, thanks for you help, great tutorial.

  • Bioche says:

    Hi Mike,

    Nice tutorial. I’m usually using container views whenI need to switch between view controllers in a bigger view controller (you already have a tutorial about that and it helped me quite a bit^^).

    However, I still use custom UIViews when I need the same view in many screens throughout the app. I was wondering about switching to container views and controllers but I’m afraid this is a bit heavy for small views that appear frequently. For example, would you recommand this practice for a control that’s repeated multiple times in the same controller (typically a custom combo box) ? I’m also wondering about performance, if you have any info on a difference between the two methods.

    Thanks a lot,
    Good day
    Eric

  • Nathan says:

    i need to drag or pan the container view on the action of pangesture. make it look like slideout menu.Code Use so for:–
    – (void)viewDidLoad {
    [super viewDidLoad];
    UIPanGestureRecognizer *swipeRightSlider = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanSlider:)];
    [self.view addGestureRecognizer:swipeRightSlider];
    }
    – (void)handlePanSlider:(UIPanGestureRecognizer *)swipe {
    CGPoint vel = [swipe velocityInView:self.view];
    if (vel.x > 0){
    NSLog(@”draggedIn”);
    }
    else{
    NSLog(@”draggedOut”);
    }
    }
    Also have taken property of container view _myContainerView

  • Norman says:

    Anyone know why a view in a container view would fail to conform to the container’s constraints?

  • Santoshi says:

    I’m using container views inside my project to display a pie on my screen board No chart data available like this coming how to render pie on that container. I’m using below code to pass data but its not coming please help me
    code:
    func call_segue()
    {
    self.performSegue(withIdentifier: “pChartviewA”, sender: self)
    }

    func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
    {
    if (segue.identifier == “pChartviewA”) {
    let pChartViewA = segue.destination as! PieChartView

  • Ruchi says:

    So, does it means that if we are using container views then we do not need to write addChildViewController(), willMoveToParentViewController(), removeFromParentViewController() stuff. Is it handled automatically then?

  • Santi says:

    Comming from Android I can’t replicate this tutorial. I guess the initial steps are missing. Do I have to add two View Controllers to the scene in the begining? How do I add the next button on the action bar? (I know how to do it, but as soon as I add a segue from the Container View the app crashes).
    How do we start?

  • Comments are closed.