What Does Addchildviewcontroller Actually Do

What does addChildViewController actually do?

I was wondering about this question too. I watched Session 102 of the WWDC 2011 videos and Mr. View Controller, Bruce D. Nilo, said this:

viewWillAppear:, viewDidAppear:, etc have nothing to do with addChildViewController:. All that addChildViewController: does is to say "This view controller is a child of that one" and it has nothing to do with view appearance. When they get called is associated with when views move in and out of the window hierarchy.

So it seems that the call to addChildViewController: does very little. The side effects of the call are the important part. They come from the parentViewController and childViewControllers relationships. Here are some of the side effects that I know:

  • Forwarding appearance methods to child view controllers
  • Forwarding rotation methods
  • (Possibly) forwarding memory warnings
  • Avoiding inconsistent VC hierarchies, especially in transitionFromViewController:toViewController:… where both VCs need to have the same parent
  • Allowing custom container view controllers to take part in State Preservation and Restoration
  • Taking part in the responder chain
  • Hooking up the navigationController, tabBarController, etc properties

To call addChildViewController or not to call addChildViewController

One of the major design assumptions in UIKit is that the ViewController hierarchy will generally be in sync with the View hierarchy. Callbacks handling autorotation and size class transitions are passed down through the ViewController chain, and if you never create the parent-child relationship between ViewControllers this system breaks down.

I follow the pattern of (psudo code)

//given:
ViewController *a;
ViewController *b;

[a willMoveToViewController:b];
[b addChildViewController:a];
[a didMoveToViewController:b];

[b.view addSubview:a.view];

Difference between addChildViewController and addSubview?

They are very different. addChildViewController associates a view controller with a parent container view controller, while addSubview adds a view to the view hierarchy of the view it is being added to. In the former case, the new child view controller will be responsible for handling events when it is the selected view controller of its parent. Think of a tab bar controller--each tab has its own associated "child" view controller that displays its view within the parent tab bar controller's content area and handles any user interaction within that view when its corresponding tab is selected in the tab bar. You should only use addChildViewController when you have a custom container view and want to add a new view controller to its childViewControllers property. If you just want to add a new view to the view hierarchy that can receive events, which is what it kind of sounds like, addSubview is the way to go. "Implementing a Container View Controller" section explains what addChildViewController is for.

When to use addChildViewController vs pushViewController

Yes, pushViewController: is for navigation controllers that manage a stack of view controllers. addChildViewController: on the other hand is part of an iOS 5 feature called "view controller containment".

The basic idea behind this is that you can embed your view controllers into other view controllers of your own (e.g. when porting an iPhone app to the iPad) and thus easily do your own implementation of things like navigation controllers, split view controllers etc.

One problem with an implementation like the one you show is that you only handle views. View controller events like orientation changes will not be passed properly down the hierarchy. View controller containment tries to ensure that all contained view controllers will get the appropriate messages, too.

Looking at your implementation you should also think about what you really want to achieve by this. A navigation controller may be the right thing or you might even show the next controller modally (see https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/PresentingaViewController.html) A bonus when using these methods (e.g. navigation controllers and modal views) is that the user is already familiar with those navigation techniques.

In any case https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/ is a good read about how to transition between view controllers.

When using view controller containment you basically have to add the view to the containing view as usual (this has to be done even if the controller is added). Then you use addChildViewController: to add the child view controller to the surrounding one. You also have to notify the child controller by didMoveToParentViewController: that it has been put into another controller. You can also use transitionFromViewController:toViewController: to exchange one view controller for another, optionally giving an animation.

How does View Controller Containment work in iOS 5?

The UIViewController docs are pretty clear on when and when not to call willMove/didMove methods. Check out the "Implementing a Container View Controller" documentation.

The docs say, that if you do not override addChildViewController, you do not have to call willMoveToParentViewController: method. However you do need to call the didMoveToParentViewController: method after the transition is complete. "Likewise, it is is the responsibility of the container view controller to call the willMoveToParentViewController: method before calling the removeFromParentViewController method. The removeFromParentViewController method calls the didMoveToParentViewController: method of the child view controller."

Also, there is an example worked out here and sample code here.

Good Luck

When to call addChildViewController

I don't know if this is what's causing your problem, but shouldn't this:

[self didMoveToParentViewController:toViewController];

be:

[toViewController didMoveToParentViewController:self];

Also, I'm not sure what you're doing with the subViewControllers array. It seems to be a duplication of the childViewControllers array that is already a property of a UIViewController.

One other thing I'm not sure is right. In your pop method your toViewController is the last controller in the _subViewControllers array. Don't you want it to be the second to last? Shouldn't the last be the one you're popping? You're popping vc, which is a controller you're passing in to the method, I don't understand that.

This is the way I've made a navigation like controller. In its containment behavior, it acts like a navigation controller, but without a navigation bar, and allows for different transition animations:

@implementation ViewController

-(id)initWithRootViewController:(UIViewController *) rootVC {
if (self = [super init]) {
[self addChildViewController:rootVC];
rootVC.view.frame = self.view.bounds;
[self.view addSubview:rootVC.view];
}
return self;
}


-(void)pushViewController:(UIViewController *) vc animation:(UIViewAnimationOptions)animation {
vc.view.frame = self.view.bounds;
[self addChildViewController:vc];
[self transitionFromViewController:self.childViewControllers[self.childViewControllers.count -2] toViewController:vc duration:1 options:animation animations:nil
completion:^(BOOL finished) {
[vc didMoveToParentViewController:self];
NSLog(@"%@",self.childViewControllers);
}];
}

-(void)popViewControllerAnimation:(UIViewAnimationOptions)animation {
[self transitionFromViewController:self.childViewControllers.lastObject toViewController:self.childViewControllers[self.childViewControllers.count -2] duration:1 options:animation animations:nil
completion:^(BOOL finished) {
[self.childViewControllers.lastObject removeFromParentViewController];
NSLog(@"%@",self.childViewControllers);
}];
}

-(void)popToRootControllerAnimation:(UIViewAnimationOptions)animation {
[self transitionFromViewController:self.childViewControllers.lastObject toViewController:self.childViewControllers[0] duration:1 options:animation animations:nil
completion:^(BOOL finished) {
for (int i = self.childViewControllers.count -1; i>0; i--) {
[self.childViewControllers[i] removeFromParentViewController];
}
NSLog(@"%@",self.childViewControllers);
}];
}

After Edit: I was able to duplicate the back button function with this controller by adding a navigation bar to all my controllers in IB (including in the one that is the custom container controller). I added a bar button to any controllers that will be pushed, and set their titles to nil (I got some glitches if I left the title as "item"). Deleting that title makes the button disappear (in IB) but you can still make connections to it in the scene list. I added an IBOutlet to it, and added this code to get the function I wanted:

- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (self.isMovingToParentViewController) {
self.backButton.title = [self.parentViewController.childViewControllers[self.parentViewController.childViewControllers.count -2] navigationItem].title;
}else{
self.backButton.title = [self.parentViewController.childViewControllers[self.parentViewController.childViewControllers.count -3] title];
}
}

I've shown two different ways that worked to access a title -- in IB you can set a title for the controller which I used in the else clause, or you can use the navigationItem title as I did in the if part of the clause. The "-3" in the else clause is necessary because at the time viewWillAppear is called, the controller that is being popped is still in the childViewControllers array.

Switch Between Child View Controllers

You can do it using protocol

protocol StartVcProtocol {
func startButtonPressed()
}

Let HomeViewController implement it

extension HomeViewController: StartVcProtocol {
func startButtonPressed() {
// start button pressed -- do your remove and add stuff here
}
}

Now in StartVc

class StartVc: UIViewController {

var delegate: StartVcProtocol?


//inside you start button iBaction
delegate?.startButtonPressed()

}

Then when lazy initializing the StartVc

viewController.delegate = self

Hope you get all the pieces .

Why do I need to call addSubview if i'm immediately following it with addChildViewController

Your main concern as I see is calling addSubview seems redundant?

I would say no it is not redundant, coz addChildViewController just Adds the given view controller as a child but it will not load/add the view.
This makes it essential to call addSubview

addSubview Add the child controller’s view as a subview.

For more info see:

Creating Custom Content View Controllers

Implementing a Custom Container View Controller



Related Topics



Leave a reply



Submit