5

My main scene shows a child UIViewController, by putting a Child Container in its View, and embedding a child UIViewController in that Child Container. I do that by ctrl-dragging from the Child Container to the child UIViewController in the storyboard, and choosing the "embed" segue.

That lets me encapsulate and reuse the child UIViewController elsewhere. I think that is fairly standard. But I can't get it to auto layout properly.

I've made a simple test case to demonstrate this: https://github.com/murraycu/ios-example-autolayout-of-child-container-views

It has two UIViewControllers that are embedded in a Tab Controller so you can switch between them.

The "Simple" tab shows the SimpleViewController, which shows an image and a label, seen here in the storyboard:

SimpleViewController

Neither the UIImage or the UILabel has a height constraint specified, though they have an Equal Width (equal to the parent view) constraint to keep the widths simple.

That UILabel is clearly not very high but the UIImage and UILabel have slightly different Content Hugging priorities, making their heights not ambiguous according to Auto Layout. So thanks to auto layout, when I set its text at runtime to some text that would require more space, it takes up more space, taking space away from the UIImage above it. That's good - it is the behaviour that I want, as seen in the emulator:

SimpleViewController in the emulator

Now to the problem: The "With Child Container" tab shows the WithChildContainerViewController, which shows the same image but shows my ChildViewController (embedded in a Child Container) instead of the UILabel. And that embedded ChildViewController shows the UILabel. Here it is in the storyboard:

WithChildContainerViewController

However, the auto layout system now doesn't seem to know how much space the Child Container needs to show all the text in the label in my ChildViewController. As in the "Simple" tab, neither the UIImage or the Child Container has a height constraint specified. Now XCode complains that "Height is ambiguous for "container view". And it looks like this in the simulator:

WithChildContainerViewController in the emulator

That's improved if I add a constraint to the Child Container, constraining its bottom to the bottom of the parent view, as suggested by @iphonic: https://github.com/murraycu/ios-example-autolayout-of-child-container-views/commit/1d295fe0a6c4502764f8725d3b99adf8dab6b9ae but the height is still wrong:

enter image description here

How can I let the auto layout system know what to do? I've thought about implementing UIView's intrinsicContentSize, but UIViewController isn't a UIView.

Bhavesh Nayi
  • 3,626
  • 1
  • 27
  • 42
murrayc
  • 2,103
  • 4
  • 17
  • 32
  • 1
    You need to add Constraints on `ChildViewController's View` with respect of the container on which you are displaying child. – iphonic Jun 23 '15 at 09:38
  • Thanks. That sounds good, but I can't seem to ctrl-drag from Child View Controller's main View to the containerView (the Child Container) in the other (parent) scene. Could you be more specific, please? – murrayc Jun 23 '15 at 09:48
  • @murrayc did you ever solve this issue? I'm experiencing the same – Lneuner Dec 22 '16 at 11:34
  • I think I fixed it in code as in iPhonic's answer, and the comments below that. I didn't find a way to do that without having layout warnings. I've done that in my ios-galaxyzoo project: https://github.com/murraycu/ios-galaxyzoo and I think I did it in the example project: https://github.com/murraycu/ios-example-autolayout-of-child-container-views . It's been a while since I thought about it. – murrayc Dec 22 '16 at 12:25

2 Answers2

3

Suggestion is, do it programatically rather than via IB. See below

 _childController=[self.storyboard instantiateViewControllerWithIdentifier:@"ChildController"];

    [self addChildViewController:_childController];
    [_container addSubview:_childController.view];
    [_childController didMoveToParentViewController:self];

    _childController.view.frame=_container.bounds;


    //add constraints        
    UIView *subView=_childController.view;
    UIView *parent=_container;


    subView.translatesAutoresizingMaskIntoConstraints=NO;

    NSLayoutConstraint *width =[NSLayoutConstraint
                                    constraintWithItem:subView
                                    attribute:NSLayoutAttributeWidth
                                    relatedBy:0
                                    toItem:parent
                                    attribute:NSLayoutAttributeWidth
                                    multiplier:1.0
                                    constant:0];
    NSLayoutConstraint *height =[NSLayoutConstraint
                                     constraintWithItem:subView
                                     attribute:NSLayoutAttributeHeight
                                     relatedBy:0
                                     toItem:parent
                                     attribute:NSLayoutAttributeHeight
                                     multiplier:1.0
                                     constant:0];
   NSLayoutConstraint *top = [NSLayoutConstraint
                                   constraintWithItem:subView
                                   attribute:NSLayoutAttributeTop
                                   relatedBy:NSLayoutRelationEqual
                                   toItem:parent
                                   attribute:NSLayoutAttributeTop
                                   multiplier:1.0f
                                   constant:0.f];

NSLayoutConstraint *leading = [NSLayoutConstraint
                                           constraintWithItem:subView
                                           attribute:NSLayoutAttributeLeading
                                           relatedBy:NSLayoutRelationEqual
                                           toItem:parent
                                           attribute:NSLayoutAttributeLeading
                                           multiplier:1.0f
                                           constant:0.f];
    [parent addConstraint:width];
    [parent addConstraint:height];
    [parent addConstraint:top];
    [parent addConstraint:leading];

Hope it helps.

Cheers.

iphonic
  • 12,615
  • 7
  • 60
  • 107
  • Thanks. I tried that in a branch here: https://github.com/murraycu/ios-example-autolayout-of-child-container-views/commits/iphonic-incode It improves things - now I see the text at least, but the label/child-view take s up too much vertical space now. I've put a screenshot at the end of the question because I can't put one here. – murrayc Jun 23 '15 at 10:31
  • @murrayc I have seen your code, all fine, but in WithChild controller you need to set bottom constraint for container with the superview, which is missing and you have layout error showing. add that, it works fine.. – iphonic Jun 23 '15 at 10:55
  • @murrayc See my fork here https://github.com/iphonic/ios-example-autolayout-of-child-container-views – iphonic Jun 23 '15 at 11:00
  • Excellent. Yes, thanks, adding this bottom constraint fixed that: https://github.com/murraycu/ios-example-autolayout-of-child-container-views/commit/88def294312bf633e304c89aae59b7f416e6f28f I've added that to the regular branch too (without the constraints in code) and I'll update the screenshots, though we still need the code to make it really work. Do you know if this can be done in just the storyboard without code? – murrayc Jun 23 '15 at 11:06
  • It would also be nice to avoid the two "Misplaced View" storyboard warnings that remain, even in your fork, about containerView's and testImage's Frames being different at run time. – murrayc Jun 23 '15 at 11:15
  • @murrayc I have updated the constraints, and updated. It should fix the misplacements. – iphonic Jun 23 '15 at 11:32
  • Thanks, but that Aspect Ratio uses a hard-coded ratio based on the position with that text, so the imageView no longer changes its size if the text changes. Anyway, I'm gladly accepting the answer for now even though I'd prefer to do this without the layout warnings, and ideally by setting the constraints in the storyboard instead of code. Many thanks. – murrayc Jun 23 '15 at 12:14
0

@iphonic's code but using Visual Format Language

_childController = [self.storyboard instantiateViewControllerWithIdentifier: @"ChildController"];

[self addChildViewController: _childController];
[_container addSubview: _childController.view];
[_childController didMoveToParentViewController: self];

//add constraints
NSDictionary *views = @{@"subView": _childController.view, 
                        @"parent": _container};

[parent addConstraints:[NSLayoutConstraint
                        constraintsWithVisualFormat: @"V:|[subView(==parent)]"
                        options: 0
                        metrics: nil
                        views: views]];

[parent addConstraints: [NSLayoutConstraint
                         constraintsWithVisualFormat:@"H:|[subView(==parent)]"
                         options: 0
                         metrics: nil
                         views: views]];
benoit
  • 71
  • 1
  • 3
  • Don't you need to list tell constraintsWithVisualFormat about subView and parent via the views parameter? – murrayc Nov 23 '15 at 13:43