Dante is not a Geek Dante is not a Geek - 6 months ago 857
iOS Question

How to add a Container View programmatically

A Container View can be easily added into a storyboard through Interface Editor. When added, a Container View is of a placeholder view, an embed segue, and a (child) view controller.

However, I am not able to find a way to add a Container View programmatically. Actually, I am not even able to find a class named

UIContainerView
or so.

A name for the class of Container View is surely a good start. A complete guide including the segue will be much appreciated.

I am aware of View Controller Programming Guide, but I do not regard it as the same as the way Interface Builder does for Container Viewer. For example, when the constraints are properly set, the (child) view will adapts to the size changes in Container View.

Rob Rob
Answer

A storyboard "container view" is just a standard UIView object. There is no special "container view" type. In fact, if you look at the view hierarchy, you can see that the "container view" is a standard UIView:

container view

To achieve this programmatically, you employ "view controller containment":

  • Instantiate the child view controller by calling instantiateViewControllerWithIdentifier on the storyboard object.
  • Call addChildViewController in your parent view controller.
  • Add the view controller's view to your view hierarchy with addSubview (and also set the frame or constraints as appropriate).
  • Call the didMoveToParentViewController method on the child view controller, passing the reference to the parent view controller.

See Implementing a Container View Controller in the View Controller Programming Guide and the "Implementing a Container View Controller" section of the UIViewController Class Reference.


For example, it might look like:

override func viewDidLoad() {
    super.viewDidLoad()

    let controller = storyboard!.instantiateViewControllerWithIdentifier("Second")
    addChildViewController(controller)
    controller.view.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(controller.view)

    NSLayoutConstraint.activateConstraints([
        controller.view.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor, constant: 10),
        controller.view.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor, constant: -10),
        controller.view.topAnchor.constraintEqualToAnchor(view.topAnchor, constant: 10),
        controller.view.bottomAnchor.constraintEqualToAnchor(view.bottomAnchor, constant: -10)
    ])

    controller.didMoveToParentViewController(self)
}

Note, the above doesn't actually add a "container view" to the hierarchy. If you want to do that, you'd do something like:

override func viewDidLoad() {
    super.viewDidLoad()

    // add container

    let containerView = UIView()
    containerView.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(containerView)
    NSLayoutConstraint.activateConstraints([
        containerView.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor, constant: 10),
        containerView.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor, constant: -10),
        containerView.topAnchor.constraintEqualToAnchor(view.topAnchor, constant: 10),
        containerView.bottomAnchor.constraintEqualToAnchor(view.bottomAnchor, constant: -10),
    ])

    // add child view controller view to container

    let controller = storyboard!.instantiateViewControllerWithIdentifier("Second")
    addChildViewController(controller)
    controller.view.translatesAutoresizingMaskIntoConstraints = false
    containerView.addSubview(controller.view)

    NSLayoutConstraint.activateConstraints([
        controller.view.leadingAnchor.constraintEqualToAnchor(containerView.leadingAnchor),
        controller.view.trailingAnchor.constraintEqualToAnchor(containerView.trailingAnchor),
        controller.view.topAnchor.constraintEqualToAnchor(containerView.topAnchor),
        controller.view.bottomAnchor.constraintEqualToAnchor(containerView.bottomAnchor)
    ])

    controller.didMoveToParentViewController(self)
}

This latter pattern is extremely useful if ever transitioning between different child view controllers and you just want to make sure one child's view is in the same location and the previous child's view (i.e. all the unique constraints for the placement are dictated by the container view, rather than needing to rebuild these constraints each time). But if just performing simple view containment, the need for this separate container view is less compelling.