Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Autolayout constraints and child view controller

I have two view controllers, parent and child.

So in the viewDidLoad method I do the following:

ChildViewController* childViewController = [[ChildViewController alloc] init];

[self addChildViewController:childViewController];

// ChildViewController sets his own constraints in viewDidLoad
[self.view addSubview:childViewController.view];

[childViewController didMoveToParentViewController:self];

//
// setup constraints to expand childViewController.view to 
// fill the size of parent view controller
//

So basically what happens is that updateViewConstraints is called on ChildViewController before parent controller constraints apply, so in fact self.view.frame == CGRectZero, exactly the same as I specified in custom loadView method in ChildViewController.

translatesAutoresizingMaskIntoConstraints all set to NO for all views.

What's the proper way to setup constraints in this case so ChildViewController updates his constraints after parent?

Current log from both controllers is pretty frustrating, I do not understand how updateViewConstraints can be called before viewWillLayoutSubviews:

App[47933:c07] ChildViewController::updateViewConstraints. RECT: {{0, 0}, {0, 0}}
App[47933:c07] ParentViewController::updateViewConstraints
App[47933:c07] ChildViewController:viewWillLayoutSubviews. RECT: {{0, 0}, {984, 454}}
App[47933:c07] ChildViewController:viewDidLayoutSubviews. RECT: {{0, 0}, {984, 454}}
like image 466
Rob Zombie Avatar asked Jul 29 '13 16:07

Rob Zombie


2 Answers

You can add constraints right after invoke of addSubview: , don't forget to set translatesAutoresizingMaskIntoConstraints to false

Here is a code snippet of adding and hiding child view controller with constraints (inspired by apple guide)

Display

private func display(contentController content : UIViewController)
    {
        self.addChildViewController(content)
        content.view.translatesAutoresizingMaskIntoConstraints = false
        self.containerView.addSubview(content.view)
        content.didMove(toParentViewController: self)

        containerView.addConstraints([
            NSLayoutConstraint(item: content.view, attribute: .top, relatedBy: .equal, toItem: containerView, attribute: .top, multiplier: 1, constant: 0),
            NSLayoutConstraint(item: content.view, attribute: .bottom, relatedBy: .equal, toItem: containerView, attribute: .bottom, multiplier: 1, constant: 0),
            NSLayoutConstraint(item: content.view, attribute: .leading, relatedBy: .equal, toItem: containerView, attribute: .leading, multiplier: 1, constant: 0),
            NSLayoutConstraint(item: content.view, attribute: .trailing, relatedBy: .equal, toItem: containerView, attribute: .trailing, multiplier: 1, constant: 0)
            ])
    }

Hide

private func hide(contentController content : UIViewController)
    {
        content.willMove(toParentViewController: nil)
        content.view.removeFromSuperview()
        content.removeFromParentViewController()
    }
like image 56
Benny Davidovitz Avatar answered Sep 20 '22 11:09

Benny Davidovitz


I have a similar situation where I add a child controller to a visible controller (a popup).

I define the child view controller in interface builder. It's viewDidLoad method just calls setTranslatesAutoresizingMaskIntoConstraints:NO

Then I define this method on the child controller which takes a UIViewController parameter, which is the parent. This method adds itself to the given parent view controller and defines its own constraints:

- (void) addPopupToController:(UIViewController *)parent {
    UIView *view = [self view];
    [self willMoveToParentViewController:parent];
    [parent addChildViewController:self];
    [parent.view addSubview:view];
    [self didMoveToParentViewController:parent];

    NSArray *horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[view]-0-|"
                                                                             options:0
                                                                             metrics:nil
                                                                               views:NSDictionaryOfVariableBindings(view)];
    [parent.view addConstraints:horizontalConstraints];

    NSArray *verticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[view]-0-|"
                                                                           options:0
                                                                           metrics:nil
                                                                             views:NSDictionaryOfVariableBindings(view)];
    [parent.view addConstraints:verticalConstraints];
}

then inside the parent UIViewController (the one which is already displayed), when I want to display my child popup view controller it calls:

PopUpNotificationViewController *popup = [[self storyboard] instantiateViewControllerWithIdentifier:@"NotificationPopup"];
[popup addPopupToController:self];

You can define whatever constraints you want on the child controller's view when adding it to the parent controller's view.

like image 30
Jonathon Horsman Avatar answered Sep 22 '22 11:09

Jonathon Horsman