TomorrowPlusX
TomorrowPlusX

Reputation: 1215

UILabel layout weirdness in UICollectionViewCell

I'm having weirdness laying out two labels in a UICollectionViewCell subclass. The prototype cell was built in InterfaceBuilder, and has no layout constraints assigned. When the controller assigns a title and date to the cell, I trigger a layout pass, in which I override the position of the labels based on their reported heights.

The weirdness is that when the cell is initially displayed, the label positions are incurrect. But when the cell is scrolled off-screen and then re-shown scrolling back on screen, the layout is correct!

Here's a link to a screen recording

I've set a translucent red on the title label, and translucent blue on the date label. The title label should be snapped to fit the text ( with a max of two lines ) and the date label should have the same width, and be right below. In the video you can see that until scrolled offscreen, the title label has an incorrect height, and the date label overlaps it.

- (void)setTitle:(NSString *)title
{
    _title = [title copy];
    self.titleLabel.text = _title;
    self.titleLabel.lineBreakMode = NSLineBreakByTruncatingTail;
    self.titleLabel.numberOfLines = 2;
    [self.titleLabel sizeToFit];
    [self setNeedsLayout];
}

- (void)setDate:(NSDate *)date
{
    _date = [date copy];
    self.dateLabel.text = [self.class.sharedShortDateFormatter stringFromDate:_date];
    self.dateLabel.lineBreakMode = NSLineBreakByWordWrapping;
    self.dateLabel.numberOfLines = 1;
    [self.dateLabel sizeToFit];
    [self setNeedsLayout];
}

- (void)layoutSubviews
{
    [super layoutSubviews];

    #warning MMBFiguresCollectionViewCell doesn't lay out its labels on first display - only works on second
    self.titleLabel.backgroundColor = [[UIColor redColor] colorWithAlphaComponent:0.0875];
    self.dateLabel.backgroundColor = [[UIColor blueColor] colorWithAlphaComponent:0.0875];

    const CGRect LabelFrame = self.titleLabel.frame;
    const CGRect StarFrame = self.favoriteButton.frame;

    self.titleLabel.frame = CGRectMake(CGRectGetMinX(LabelFrame), CGRectGetMinY(LabelFrame), CGRectGetMinX(StarFrame) - CGRectGetMinX(LabelFrame), CGRectGetHeight(LabelFrame));
    self.dateLabel.frame = CGRectMake(CGRectGetMinX(self.titleLabel.frame), CGRectGetMaxY(self.titleLabel.frame), CGRectGetWidth(self.titleLabel.frame), CGRectGetHeight(self.dateLabel.frame));

    NSLog(@"self.titleLabel.frame: %@ dateLabel.frame: %@", NSStringFromCGRect(self.titleLabel.frame), NSStringFromCGRect(self.dateLabel.frame));
}

When I watch the frames reported in the NSLog statement, I see frame values that appear correct. When scrolling off-screen and back on, the frame values are the same - so I know that the frames are being correctly computed.

I have to assume, then, that CALayer bitmap caching ( or some such thing ) is caching a pre-layout rendering of the view?

NOTE: I chose to position labels in -layoutSubviews and use setNeedsLayout for efficiency reasons. I also tried (just now) factoring the label positioning into a separate method and invoking it directly when the title and date labels change - but it had the same effect.

Upvotes: 2

Views: 291

Answers (1)

TomorrowPlusX
TomorrowPlusX

Reputation: 1215

So, I gave up on using "clever" manual layout and just used well-chosen layout constraints from IB -- and lo, it worked.

I honestly didn't think I'd be able to successfully wield auto layout in this context, but it worked. Who knew?

Upvotes: 1

Related Questions