UILabel有时无法正确包装文本(自动布局)

Bob*_*ryn 40 ios autolayout

我在视图中有一个uilabel设置.它没有宽度约束,但其宽度由缩略图图像的前导约束和视图边缘的尾随约束决定.

在此输入图像描述

标签设置为0行,并自动换行.我的理解是,这应该使uilabel的框架成长,有时它确实会发生.(在自动布局之前,我会计算并更新代码中标签的框架).

结果是,它在某些情况下可以正常工作,而在其他情况下则不然.看到大多数细胞在那里正常工作,但最后一个细胞看起来太大了.实际上它的尺寸合适.标题"Fair Oaks Smog Check Test"实际上以"Only"结尾.所以我对单元格大小的计算是正确的,它应该是那么大.但是,无论出于何种原因,标签都不会包装文本.它的框架宽度不会向右延伸,所以这不是问题.

在此输入图像描述

那么这里发生了什么?它是100%一致的,总是在那个单元格而不是它上面的单元格,这让我认为它与文本的大小有关,并且一旦将该视图添加到单元格中,UILabel就不会重新布置文本(其中使它实际上更小的宽度).

有什么想法吗?

一些额外的信息

从我创建的一个样本单元计算单元格的高度,并将其存储在静态变量中:

- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    if (self.items.count == 0) {
        return 60;
    }
    static TCAnswerBuilderCell *cell = nil;
    static dispatch_once_t pred;

    dispatch_once(&pred,
                  ^{
                      // get a sample cellonce
                      cell = [tableView dequeueReusableCellWithIdentifier:TC_ANSWER_BUILDER_CELL];
                  });
    [cell configureCellWithThirdPartyObject:self.items[indexPath.row]];
    return [cell heightForCellWithTableWidth:self.tableView.frame.size.width];
}
Run Code Online (Sandbox Code Playgroud)

我在运行中使用我的数据对象配置单元格,然后调用我在其上的方法,该方法计算具有给定表格宽度的单元格的高度(不能总是依赖于最初正确的单元格框架).

这反过来在我的视图上调用高度方法,因为它实际上是标签所在的位置:

- (CGFloat)heightForCellWithTableWidth:(CGFloat)tableWidth {
    // subtract 38 from the constraint above
    return [self.thirdPartyAnswerView heightForViewWithViewWidth:tableWidth - 38];
}
Run Code Online (Sandbox Code Playgroud)

此方法通过确定标签的正确宽度来确定高度,然后进行计算:

- (CGFloat)heightForViewWithViewWidth:(CGFloat)viewWidth {
    CGFloat widthForCalc = viewWidth - self.imageFrameLeadingSpaceConstraint.constant - self.thumbnailFrameWidthConstraint.constant - self.titleLabelLeadingSpaceConstraint.constant;
    CGSize size = [self.titleLabel.text sizeWithFont:self.titleLabel.font constrainedToSize:CGSizeMake(widthForCalc, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping];
    CGFloat returnHeight = self.frame.size.height - self.titleLabel.frame.size.height + size.height;
    CGFloat height = returnHeight < self.frame.size.height ? self.frame.size.height : returnHeight;
    return height;
}
Run Code Online (Sandbox Code Playgroud)

这100%正确工作.

单元格显然在cellForRowAtIndexPath中创建并立即配置:

if (self.items.count > 0) {
    TCAnswerBuilderCell *cell = [tableView dequeueReusableCellWithIdentifier:TC_ANSWER_BUILDER_CELL forIndexPath:indexPath];
    [cell configureCellWithThirdPartyObject:self.items[indexPath.row]];
    return cell;
}
Run Code Online (Sandbox Code Playgroud)

在单元格的配置中,我的视图是从一个笔尖加载的(它在别处重新使用,这就是为什么它不直接在单元格中).单元格添加如下:

- (void) configureCellWithThirdPartyObject:(TCThirdPartyObject *)object {
    self.detailDisclosureImageView.hidden = NO;
    if (!self.thirdPartyAnswerView) {
        self.thirdPartyAnswerView = [TCThirdPartyAPIHelper thirdPartyAnswerViewForThirdPartyAPIServiceType:object.thirdPartyAPIType];
        self.thirdPartyAnswerView.translatesAutoresizingMaskIntoConstraints = NO;
        [self.contentView addSubview:self.thirdPartyAnswerView];
        [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|[_thirdPartyAnswerView]-38-|" options:NSLayoutFormatAlignAllCenterY metrics:nil views:NSDictionaryOfVariableBindings(_thirdPartyAnswerView)]];
    }
    [self.thirdPartyAnswerView configureViewForThirdPartyObject:object forViewStyle:TCThirdPartyAnswerViewStyleSearchCell];
}
Run Code Online (Sandbox Code Playgroud)

最后我的视图配置如下所示:

- (void) configureViewForThirdPartyObject:(TCTPOPlace *)object forViewStyle:(TCThirdPartyAnswerViewStyle) style {
    self.titleLabel.text = object.name;
    self.addressLabel.text = [NSString stringWithFormat:@"%@, %@, %@", object.address, object.city, object.state];
    self.ratingsLabel.text = [NSString stringWithFormat:@"%d Ratings", object.reviewCount];
    NSString *ratingImageName = [NSString stringWithFormat:@"yelp_star_rating_%.1f.png", object.rating];
    UIImage *ratingsImage = [UIImage imageNamed:ratingImageName];
    if (ratingsImage) {
        self.ratingImageView.image = ratingsImage;
    }
    if (object.imageUrl) {
        [self.thumbnailImageView setImageWithURL:[NSURL URLWithString:object.imageUrl] completed:nil];
    }
}
Run Code Online (Sandbox Code Playgroud)

一种解决方案,但我不明白为什么

  1. 我的子视图设计为320宽度,但宽度没有自己的约束
  2. 子视图已添加到单元格中,但水平约束如下所示:
    • @"|[_thirdPartyAnswerView]-38-|"
  3. 视图在添加到单元格后立即配置,这意味着titleLabel的文本设置正确.
  4. 无论出于何种原因,文本都被布置为好像视图具有完整的320而不是282.
  5. 即使子视图的框架更新为282,标签也从未更新过,并且标签上有一些限制条件可以使其大小正确.
  6. 将xib中视图的大小更改为282修复了问题,因为标签的大小合适.

我仍然不理解为什么在父视图具有前导和尾随约束时更新父视图的大小后标签不会重新布局.

解决了

请参阅下面的Matt答案:https://stackoverflow.com/a/15514707/287403

如果你没有阅读评论,主要的问题是我preferredMaxLayoutWidth在设计一个比它显示的宽度更大的视图(在某些情况下)时不知不觉地通过IB 进行设置.preferredMaxLayoutWidth是什么用于确定文本包装的位置.因此,即使我的视图和titleLabel正确调整大小,preferredMaxLayoutWidth仍然处于旧值,并导致包装在意外点.将titleLabel设置为自动大小(⌘=在IB中),并在调用super之前preferredMaxLayoutWidth动态更新layoutSubviews是关键.谢谢马特!

mat*_*att 42

我是一个编写了一个应用程序的人,该应用程序在表格中单元格具有不同高度的单元格中使用自动布局的五个标签,其中标签根据其中的内容自行调整大小,并且确实有效.因此,我将建议你遇到麻烦的原因可能是你的约束不足以确定布局 - 也就是说,你对单元格元素的布局不明确.我无法检验这个假设,因为我无法看到你的约束.但是你可以通过po [[UIWindow keyWindow] _autolayoutTrace]在调试器中暂停时使用来轻松检查(我认为).

另外我还有另外一个建议(抱歉只是向你扔东西):确保你正在设置标签preferredMaxLayoutWidth.这是至关重要的,因为它是标签停止水平生长并开始垂直生长的宽度.


pha*_*ann 13

我有同样的问题,并使用这个答案的建议解决了它.在UILabel的子类中,我放置了以下代码:

- (void)layoutSubviews
{
    [super layoutSubviews];
    self.preferredMaxLayoutWidth = self.bounds.size.width;
}
Run Code Online (Sandbox Code Playgroud)

我不明白为什么这不是UILabel的默认行为,或者至少为什么你不能通过标志启用此行为.

我有点担心preferredMaxLayoutWidth是在布局过程中设置的,但我认为没有简单的方法.

  • @RyanM:在IB中,您可以选择"自动"作为首选布局宽度. (2认同)