Centurion
Centurion

Reputation: 14304

How to change label constraints during runtime?

I have a table view and a cell inside it. The cell contains three labels: header label and two labels below one to each other. Sometimes, I need to hide those two labels below if they do not contain data and change "Top space to container" of "Header label" to "Center Y to container". And of course revert the constraints back when two labels contain data. Here's a screenshot of simple demo project just for showing the idea:

enter image description here

UPDATE Max MacLeod answer pointed to right direction. The trick is to push header label down when first and second labels are hidden. So we need to set bottom space to container view for first and second labels instead of creating top space to container view for header label. And hiding/unhiding should be done by having height outlets (height constraints for first and second label) and setting their constant values to zero (and setting back value when unhiding). I also uploaded the source code example to Github.

Upvotes: 19

Views: 24924

Answers (4)

Jorge Arimany
Jorge Arimany

Reputation: 5882

Put the labels you want to hide into a view, once everything has the correct layout constraints, add a height constraint to the container view, and connect the constraint into an IBOutlet property.

Make sure that your properties are strong

in code yo just have to set the constant to 0 and activate it, tho hide the content, or deactivate it to show the content. This is better than messing up with the constant value an saving-restoring it. Do not forget to call layoutIfNeeded afterwards.

@property (strong, nonatomic) IBOutlet UIView *myContainer;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *myContainerHeight; //should be strong!!

-(void) showContainer
{
    self.myContainerHeight.active = NO;
    self.myContainer.hidden = NO;
    [self.view layoutIfNeeded];
}
-(void) hideContainer
{
    self.myContainerHeight.active = YES;
    self.myContainerHeight.constant = 0.0f;
    self.myContainer.hidden = YES;
    [self.view layoutIfNeeded];
}

Once you have your setup you can test it in IntefaceBuilder by setting your constraint to 0 and then back to the original value. Don't forget to check other constraints priorities so when hidden there is no conflict at all. other way to test it is to put it to 0 and set the priority to 0, but, you should not forget to restore it to the highest priority again.

Upvotes: 8

Max MacLeod
Max MacLeod

Reputation: 26652

Select the header label, and one of the lower labels, and add a new vertical space constraint reflecting the gap between them. Next, remove the header label Top space to container constraint. Maybe you already have this (can't quite see from the screen grab). If you do, that's good.

Now, create two height constraints for each lower label. IBOutlet those to your class.

Then, hide those two lower labels whenever you need to by setting each height constraint's constant to 0.f.

That will render them invisible and lower the header label above so that it is now vertically centered Y in the container.

Will go through the steps once more (was too long for a comment!). Sequence matters with IB as first you must add a new constraint before you can delete the old one. Temporarily you will have one superfluous constraint. It's because IB won't allow ambiguity. So, first add the new vertical space constraint. That will define the Y position of the upper label. Then, remove the superfluous vertical space to container constraint from the upper label. Now that label will be Y positioned using the vertical space relative to the lower labels. Next, add the height constraints for each lower label and link to the class with an IBOutlet. One other thing, actually you will need the lower labels to be constrained to the container with a bottom space constraint. When their height is reduced to zero, they will disappear, and the upper label will move lower to assume the Y center position.

To revert, just set the constant back to the original value.

This is a much better approach than adding/removing constraints, which is a heavyweight operation. Note that you may wish to add the two lower labels to a "container" view, so that they can be shown/hidden as one. Also, it would tidy the code as you really want the vertical space to be between your upper label, and both lower labels rather than just one.

See also my answer AutoLayout with hidden UIViews?

Upvotes: 34

David Hoerl
David Hoerl

Reputation: 41642

I'm doing something very much like this myself. So add a width constraint to both labels. You are allowed to change the constant value while the constraint is applied - just keep a reference to it. In my case I have an array into which I put the constraints I plan to change.

When you want to hide the labels, change the c value to 0 - you can do this in an animation block too. To show change the value.

You could also make the width of the top space equal to the bottom space, so when the views hide or show you keep the "group" of items centered.

Note that you can add or delete constraints too but its more costly for iOS to deal with.

Upvotes: 0

Dumoko
Dumoko

Reputation: 2644

Rarely you can fin smth easy to understand and some hands-on examples when it is up to Apple Documentation, but this is the exact case of the exception in the rule.

I personally found this document so easy to understand, and so clear. The best way to become an auto-layout guru in time :)

https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/AutoLayoutinCode/AutoLayoutinCode.html

Basically, runtime means "programmatically".

So the answer to your question lies here:

https://stackoverflow.com/a/12623006/517119

Upvotes: -3

Related Questions