4

ParentView contains ChildView1 and ChildView2. These subviews are of different heights.

ChildView1 is taller than ChildView2. Only one subview is shown, e.g., if ChildView1 is visible then ChildView2 is hidden. Both ChildView1 and ChildView2 use custom XIBs.

Both subviews "drive" the height of ParentView, that is the AutoLayout constraints are wired such that ParentView is as tall as ChildView1 or ChildView2, and no taller.

The problem is hiding ChildView1 and showing ChildView2 does not "shrink" ParentView to match the height of ChildView2. It remains at the height of the taller subview, ChildView1.

Calling sizeToFit() and setNeedsLayout() does not change things.

How to force ParentView to match ChildView2's height when ChildView1 is hidden?

Crashalot
  • 33,605
  • 61
  • 269
  • 439

3 Answers3

7

Hidden views still participate in layout. You need to deactivate the constraints on the hidden view in addition to hiding it.

If your deployment target is iOS 9 or later, you can make the parent be a UIStackView. A stack view automatically ignores its hidden children during layout.

UPDATE

You don't need separate outlets for all the constraints. You just need two outlet collections. You can connect one outlet collection to multiple objects. Demo:

creating outlet collections

The collections end up connected to multiple objects in the storyboard:

view controller connections

Then you can activate or deactivate a collection of constraints with one statement:

class ViewController: UIViewController {

    @IBOutlet var pinkConstraints: [NSLayoutConstraint]!
    @IBOutlet var greenConstraints: [NSLayoutConstraint]!

    func showPink() {
        NSLayoutConstraint.deactivateConstraints(greenConstraints)
        NSLayoutConstraint.activateConstraints(pinkConstraints)
    }

    func showGreen() {
        NSLayoutConstraint.deactivateConstraints(pinkConstraints)
        NSLayoutConstraint.activateConstraints(greenConstraints)
    }

}
rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • thanks for the suggestion! but would updating the parent's frame as suggested by luffy also be considered okay? if so, that approach seems better since you don't need to create outlets to each constraint (there are four to "drive" the superview's height) on the hidden view. – Crashalot Apr 27 '16 at 01:55
  • That depends on whether the parent has `translatesAutoresizingMaskIntoConstraints` set. – rob mayoff Apr 27 '16 at 01:57
  • what should the value be if using luffy's approach? the solution used before you guys replied was to insert another layer between parentview and childview, so that childview drives the view of its intermediate layer and not parent view. of these three approaches (yours, luffy's, and the middle man), which do you think is cleanest? thanks again for your input! – Crashalot Apr 27 '16 at 02:00
  • What determines the child view's size? – rob mayoff Apr 27 '16 at 02:01
  • its subviews. childview1 has ~10 different components driving its height. childview2 has 2. – Crashalot Apr 27 '16 at 02:02
  • I would use a `UIStackView` because I only target iOS 9 now, but I've updated my answer to show how you can connect to multiple constraints and activate or deactivate them all together. – rob mayoff Apr 27 '16 at 02:16
  • thanks for the detailed answer! didn't use your solution but gave you credit anyway for providing such a comprehensive response. do you and 200K+ points of wisdom happen to also know the answer to this problem? http://stackoverflow.com/questions/36728704/delay-when-using-instantiateviewcontrollerwithidentifier-but-not-performseguewit – Crashalot Apr 27 '16 at 03:27
1

You can set the parent view to hold the same size as the child views

// Match to childView1
CGSize size = childView1.frame.size;
[parentView setFrame:CGRectMake(0,0,size.width,size.height)];

Or conversely

// Match to childView2
CGSize size = childView2.frame.size;
[parentView setFrame:CGRectMake(0,0,size.width,size.height)];
Luffy
  • 401
  • 5
  • 10
0

Solved the problem by inserting middle views between ParentView and ChildView1/ChildView2. That way, ChildView1 is driving the height of its containing view, not ParentView, and the same with ChildView2. Perhaps not the cleanest, but it's working. Rob's suggestion to use UIStackView seems ideal, but it requires only ditching support of iOS 8, which is not practical at the moment for the app.

Crashalot
  • 33,605
  • 61
  • 269
  • 439