What does addChildViewController actually do?
It is the first step of view containment, a process by which we keep the view hierarchy in sync with the view controller hierarchy, for those cases where we have a subview that has encapsulated its logic in its own view controller (to simplify the parent view controller, to enable a reusable child view with its own logic, etc.).
So, addChildViewController adds the child view controller to an array of childViewControllers, which keeps track of the children, facilitates them getting all the view events, keeps a strong reference to the child for you, etc.
But note, addChildViewController is only the first step. You also have to call didMoveToParentViewController, too:
- (void)showViewController:(UIViewController *)newViewController {
UIViewController* oldViewController = [self.childViewControllers objectAtIndex:0];
[oldViewController willMoveToParentViewController:nil]; // tell it you are going to remove the child
[oldViewController.view removeFromSuperview]; // remove view
[oldViewController removeFromParentViewController]; // tell it you have removed child; this calls `didMoveToParentViewController` for you
newViewController.view.frame = self.view.bounds; // use `bounds`, not `frame`
newViewController.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; // be explicit regarding resizing mask if setting `frame`
[self addChildViewController:newViewController]; // tell it that you are going to add a child; this calls `willMoveToParentViewController` for you
[self.view addSubview:newViewController.view]; // add the view
[newViewController didMoveToParentViewController:self]; // tell it that you are done
}
As an aside, please note the sequence of calls, in which the order that you call these is important. When adding, for example, you call addChildViewController, addSubview, and didMoveToParentViewController, in that order. Note, as the documentation for didMoveToParentViewController says:
If you are implementing your own container view controller, it must call the didMoveToParentViewController: method of the child view controller after the transition to the new controller is complete or, if there is no transition, immediately after calling the addChildViewController: method.
And, if you are wondering why we don't call willMoveToParentViewController in this case, too, it is because, as the documentation says, addChildViewController does that for you:
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.
Likewise, when removing, you call willMoveToParentViewController, removeFromSuperview, and removeFromParentViewController. As the documentation for willMoveToParentViewController says:
If you are implementing your own container view controller, it must call the willMoveToParentViewController: method of the child view controller before calling the removeFromParentViewController method, passing in a parent value of nil.
And, again, if you are wondering why we don't call didMoveToParentViewController when removing the child, that is because, as the documentation says, removeFromParentViewController does that for you:
The removeFromParentViewController method automatically calls the didMoveToParentViewController: method of the child view controller after it removes the child.
FYI, if animating the removal of the subview, put the call to removeFromParentViewController in the animation completion handler.
But if you perform the correct sequence of containment calls, outlined above, then the child will receive all of the appropriate view-related events.
For more information (in particular, why these willMoveToParentViewController and didMoveToParentViewController calls are so important), see WWDC 2011 video Implementing UIViewController Containment. Also see the Implementing a Container View Controller section of the UIViewController documentation.
As a minor observation, make sure that when you are adding the child’s view as a subview, reference the bounds of the parent view controller’s view, not the frame. The frame of the parent’s view is in the coordinate system of its superview. The bounds is in its own coordinate system.
You might not notice the difference when the parent view occupies the full screen, but as soon as you employ this in a scenario where the parent’s view doesn't happen to take up the full screen, you will start to encounter frame misalignment. Always use bounds when setting up coordinates for children. (Or use constraints, which gets you out of that silliness, altogether.)
Perhaps needless to say, if you want to just add the child when the parent is instantiated, one can do view controller containment entirely in storyboards without any of these add/remove and willMove/didMove calls at all. Just use “Container View” and pass whatever data is needed by the child during initialization using prepareForSegue.

For example, if the parent had a property called bar and you wanted to update a property called baz in the child:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.destinationViewController isKindOfClass:[ChildViewController class]]) {
ChildViewController *destination = segue.destinationViewController;
destination.baz = self.bar;
}
}
Now, if you want to programmatically add/remove children, then use as outlined above. But storyboard “Container View” can handle all the view containment calls for simple scenarios with very little code.