Unable to move the Y position of UILabel with Autolayout & ldquo; on & rdquo; in view

advertisements

I must be missing something terribly obvious here, but this has been an issue that's been frustrating me for DAYS.

In an iOS project on xcode 4.5, I've got several labels in a XIB, one on top of the other, in a UIScrollView that occupies a UIView. Each label is as wide as the view, and each is about 20 px above the next. On occasion, one of the labels doesn't have any information, so it gets set to invisible, and the labels below it are supposed to move up to occupy the blank space.

The problem is, if autolayout is checked "off" on the view, the labels move up exactly as they should, though the UIScrollView no longer scrolls. If it is on, the labels do not move at all, no matter what.

Here is the code... I basically just use a quick function to move each label up by the height of the invisible label.

[self moveObjectBy: self.festNameLabel moveByY:-(yearsLabel.frame.size.height-2)];

// this just quickly moves a label.
- (void)moveObjectBy:(UIView *)lbl moveByY:(int)byHeight {
    CGRect newFrame = lbl.frame;
    NSLog(@"%f, %d", newFrame.origin.y, byHeight);
    newFrame.origin.y += byHeight; //yearsLabel.frame.size.height;
    lbl.frame = newFrame;
}

When it's run, the NSLog shows it's Y has moved, but it doesn't move on the screen. I'm sure it has to do with the vertical space constraint, but it won't let me delete the constraint, and it won't let me change it to anything other than space from the top of the view. Like I said, I'm sure I'm missing something, but I've exhausted everything I know to do...


If you have just one label that might be hidden, you can move the ones below it, up with the following:

 -(void)contract {
    self.label2.hidden = YES;
    self.con2To1.constant = -34;
}

The top label has a constraint to the top of the scroll view, and all the other labels have 20 point vertical distance constraints to the ones above and below them. In this example, I'm hiding label2. The labels are all 34 points high, so changing the constraint constant from 20 to -34 moves the now hidden label right on top of the one above it.

To use this method with multiple labels that can be hidden, you will need to have an outlet to each of the labels you want to hide, and to its constraint to the label above. You actually can do it without outlets to the constraints, but I don't know if it would be as robust (it's possible that it might pick the wrong constraint). I was able to do the same thing by looping through the constraints to find the one that goes with a particular label, and is the one to the top of that label:

-(void)hideLabel:(UILabel *) label {
    label.hidden = YES;
    for (NSLayoutConstraint *con in self.scroller.constraints) {
        if (con.firstItem == label  && con.firstAttribute == NSLayoutAttributeTop) {
            con.constant = -34;
        }
    }
}

The same method can be modified to use an animation if you like -- the following snippet fades out the label you want to hide while animating the move up of all the lower labels.

 -(void)hideLabel:(UILabel *) label {
    for (NSLayoutConstraint *con in self.scroller.constraints) {
        if (con.firstItem == label  && con.firstAttribute == NSLayoutAttributeTop) {
            con.constant = -34;
            [UIView animateWithDuration:.5 animations:^{
                label.alpha = 0;
                [self.scroller layoutIfNeeded];
            }];
        }
    }
}