Autolayout constraints and child view controller

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.


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()
    }

  1. I think the layoutIfNeeded method will only work if you have previously called setNeedsLayout. If you use setNeedsLayout instead, the system will know to update at an appropriate time. Try changing that in your code.

  2. When you add constraints to a view, that view should automatically layout its subviews again to account for the new constraints. There should be no need to call setNeedsLayout unless something has changed since you have added the constraints. Are you adding the constraints to a view and if so: are you adding them to the right view?

  3. One thing you can try is to subclass UIView so that you see a log message whenever the ChildViewController.view or ParentViewController.view performs a fresh layout:

    -(void) layoutSubviews {
        [super layoutSubviews];
        NSLog(@"layoutSubviews was called on the following view: %@", [view description]);
    }
    

Maybe that will reveal something about when your views are (or aren't) layout out their subviews.