7

I have a custom UIView. Within that UIView I have a UILabel with a yellow background containing @"8" called labelWhite. I programmatically create 4 constraints for that UILabel: top, left, width, and height.

When I tap the UIView, I change the constant value of the height and animate the layout over 5 seconds. However, the height of the view immediately jumps to the new value, but the y position also changes and jumps to a new value immediately. Then over the 5 second animation, the y position animates back to where it should have stayed all along.

You can see a video of it happening here: http://inadaydevelopment.com/stackoverflow/Constraints0.mov

What it SHOULD do is just remain at y=0 and shrink vertically. What am I doing wrong?


EDIT:

I just discovered that my animations work exactly as intended if my subviews are UIViews, but as UILabels I get the jump-size + animate-position.

What is going on? Is there a way I can get UILabels to animate their size?


This is the code I use to modify and animate the layout:

self.constraintWhiteHeight.constant = secondaryHeight;

[UIView animateWithDuration:5.0 animations:^{
    [self layoutIfNeeded];
}];

This is the code I use to create the constraints:

// ------ Top -----
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self
                                                              attribute:NSLayoutAttributeTop
                                                              relatedBy:NSLayoutRelationEqual
                                                                 toItem:self.labelWhite
                                                              attribute:NSLayoutAttributeTop
                                                             multiplier:1.0
                                                               constant:0];
[self addConstraint:constraint];

// ------ Left -----
self.constraintWhiteLeft = [NSLayoutConstraint constraintWithItem:self
                                                        attribute:NSLayoutAttributeLeft
                                                        relatedBy:NSLayoutRelationEqual
                                                           toItem:self.labelWhite
                                                        attribute:NSLayoutAttributeLeft
                                                       multiplier:1.0
                                                         constant:0];

// ------ Width -----
NSString *constraintString = [NSString stringWithFormat:@"H:[_labelWhite(==%.0f)]", self.bounds.size.width];
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:constraintString
                                                               options:0
                                                               metrics:nil
                                                                 views:NSDictionaryOfVariableBindings(_labelWhite)];
NSLog(@"constraints: %@", constraints);
self.constraintWhiteWidth = [constraints objectAtIndex:0];
[self.labelWhite addConstraints:constraints];

// ------ Height -----
constraintString = [NSString stringWithFormat:@"V:[_labelWhite(==%.0f)]", primaryHeight];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:constraintString
                                                      options:0
                                                      metrics:nil
                                                        views:NSDictionaryOfVariableBindings(_labelWhite)];
self.constraintWhiteHeight = [constraints objectAtIndex:0];
[self.labelWhite addConstraints:constraints];
Kenny Wyland
  • 20,844
  • 26
  • 117
  • 229

1 Answers1

2

I don't think you're doing anything wrong, this seems to be the way that labels behave when you try to animate their height. I know I've wrestled with this problem in the past, and I can't remember if I've ever found a solution that works by animating the constraints in an animation block. The way I have gotten it to work is to use a timer or CADisplayLink to adjust the height constraint.

-(void)shrinkLabel {
    self.constraintWhiteHeight.constant -= 1;
    if (self.constraintWhiteHeight.constant >= secondaryHeight)
    [self performSelector:@selector(shrinkLabel) withObject:nil afterDelay:.05];
}

After Edit:

Although the timer method works, it doesn't always look smooth, depending on the speed and increments you use. Another way to do this is to use a large UIView (the same size as the yellow label in your movie) with a smaller UILabel inside that has centerX and centerY constraints to the larger view. Then, animate the yellow view as usual with animateWithDuration:animations:

enter image description here

-(void)shrinkLabel {
    self.yellowViewHeightCon.constant = secondaryHeight;
    [UIView animateWithDuration:5 animations:^{
        [self layoutIfNeeded];
    }];
}
rdelmar
  • 103,982
  • 12
  • 207
  • 218
  • Interesting way around the issue. Thanks, I'll play with that a bit. – Kenny Wyland May 21 '14 at 20:25
  • The second, `layoutIfNeeded` method is the way I use in my question. It works beautifully for UIVIews, but UILabels are drawn differently and it doesn't work for those. – Kenny Wyland May 23 '14 at 16:46
  • @KennyWyland, but it does work in the setup I show in my edit, where what I'm animating is the outer (yellow) UIView, and the smaller label stays properly centered as the view shrinks. Is this not what you want? – rdelmar May 23 '14 at 16:50
  • The outer view is going to stay the same size, but the inner view is going to move and change size. When it changes size, I want the content of it (the "8") to change size along with it. The problem turns out to be that UILabels don't change the size of the text during size animations, so it jumps to the new end-size-font and then animates to the desired size. – Kenny Wyland May 23 '14 at 22:51
  • @KennyWyland, ok, that's not what you show in your movie, the text is staying the same size there, so that's what I thought you wanted. – rdelmar May 23 '14 at 22:53
  • ah yes, true. That video was just to display the strange jump+animate, but I didn't include a font size change. – Kenny Wyland May 26 '14 at 05:22