Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't move UILabel's Y position with Autolayout "on" in view

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...

like image 725
Cyprus106 Avatar asked Dec 24 '12 20:12

Cyprus106


2 Answers

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];
            }];
        }
    }
}
like image 120
rdelmar Avatar answered Oct 18 '22 19:10

rdelmar


With autolayout in place, you can't set frames on subviews - at best, it will work temporarily until the next layout pass has occurred. At that point , the layout defined by your constraints will be re-established.

What you have to do is modify the constraints that are in place on the view. In your case, it would be sensible to have a specific pin-to-height constraint in place on each label. You can then create outlets to each of these constraints. For a zero-height label, you'd then set the constant on the relevant constraint to zero. The labels beneath will then move up.

Depending on the label that you are removing, this may then leave you with a double-wide gap. You can remove this by modifying the spacing constraints, or you can get rid of the spacing constraints altogether and just have each label a bit taller, but right next to each other - the content of the label will be vertically centred anyway.

The main message is that you don't set frames. You modify, add and/or remove constraints instead.

like image 30
jrturton Avatar answered Oct 18 '22 19:10

jrturton