Centurion
Centurion

Reputation: 14304

Autolayout: how to hide UIView containing subViews?

The best solution for hiding views with the new Autolayout is definitely to create height constraint for the view, connect it and create an outlet for it, and change self.myViewHeightConstriant.constant equals to 0. But suppose the view contains some other views, suppose an imageView and some label below it. Now, the imageView is 10px away from the top and has top space to superview constraint with 10px value. Trying to hide container UIView with constant = 0 shows an error in console:

Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.   
Try this: (1) look at each constraint and try to figure out which you don't expect; 
(2) find the code that added the unwanted constraint or constraints and fix it.   
(Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't 
understand, refer to the documentation for the UIView property 
translatesAutoresizingMaskIntoConstraints) 
(
   <NSLayoutConstraint:0xc7cedb0 V:[UIView:0xc7ce1e0(0)]>,
   <NSLayoutConstraint:0xc7ceea0 V:[UIImageView:0xc7ce270]-(0)-|   (Names:  '|':UIView:0xc7ce1e0 )>,
   <NSLayoutConstraint:0xc7cef30 V:|-(10)-[UIImageView:0xc7ce270]   (Names: '|':UIView:0xc7ce1e0 )>
)

Will attempt to recover by breaking constraint 

  <NSLayoutConstraint:0xc7ceea0 V:[UIImageView:0xc7ce270]-(0)-|   (Names: '|':UIView:0xc7ce1e0 )> 

The guess the problem is container UIView has height 0, but the imageView has top space offset 10px from it and Autolayout engine doesn't understand how to handle this situation. Tried to set clipSubviews for container view but that didn't help. Any ideas?

UPDATE several thoughts, creating an outlet topSpaceToSuperView constraint for the imageView and set its constaint also to 0 doesn't look very appealing. There should be more elegant solution than trashing the code with multiple outlets...

Upvotes: 0

Views: 1761

Answers (2)

Avi Frankl
Avi Frankl

Reputation: 211

If you have a leading, trailing, top, or bottom constraint you can set just set the relation to LessThanOrEqual when you hide it, and then back to equal when you show it.

Because relation is read-only, you'd do this by:

  1. Outletting the constraint
  2. Programmatically removing it from the superview
  3. Setting the outlet equal to a new constraint that has the same parameters, except with your desired <= or = (depending on if you are hiding or showing
  4. Re-adding the constraint to the superview

Essentially, all you're doing here is making that constraint small enough so that the height of your "big" view can == 0 when it's hidden.

Upvotes: 0

Kyle Truscott
Kyle Truscott

Reputation: 1577

You can't go simple with container.hidden = YES?

Otherwise, it's the bottom constraint that's breaking things. @"V:|-10-[imageView]|" tells the container view that has to be at least 10 pts tall. But @"V:|-10-[imageView]" would be fine.

Perhaps instead of anchoring the imageView to the bottom of the container, setup a constraint for the imageView's height.

[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-10-[imageView]"
                                        options:nil
                                        metrics:nil
                                          views:views];

[NSLayoutConstraint constraintWithItem:self.imageView
                             attribute:NSLayoutAttributeHeight
                             relatedBy:NSLayoutRelationEqual
                                toItem:self.containerView
                             attribute:NSLayoutAttributeHeight
                            multiplier:1.
                              constant:-10.f];

Update

You mention in the comments that the imageView isn't a predictable height. Since that's the case, it might be easier to just manage the container's height, but do it with separate constraints:

containerOpen = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-10-[imageView]|"
                                                      options:nil
                                                      metrics:nil
                                                        views:views];

containerClosed = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[containerView(0)]"
                                                        options:nil
                                                       metrics:nil
                                                         views:views];

// Toggle between the constraints to open close the container

- (void)toggleContainer
{
  [self.containerView.superview removeConstraints:containerOpen];
  [self.containerView.superview removeConstraints:containerClosed];

  self.containerView.isOpen = !self.containerView.isOpen;

  if (self.containerView.isOpen)
    [self.containerView.superview addConstraints:containerOpen];
  else
    [self.containerView.superview addConstraints:containerClosed];

  [self.containerView.superview setNeedsUpdateConstraints];
  [self.containerView setNeedsLayout];
  [self.containerView layoutIfNeeded];
}

Upvotes: 1

Related Questions