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

时间:2013-03-20 01:41:10

标签: ios autolayout

我在视图中设置了uilabel。它没有宽度约束,但其宽度取决于缩略图图像的前导约束和视图边缘的尾随约束。

enter image description here

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

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

enter image description here

那么这里发生了什么?它是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];
}

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

这反过来在我的视图中调用高度方法,因为它确实存在于标签所在的位置:

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

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

- (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;
}

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;
}

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

- (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];
}

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

- (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];
    }
}

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

  1. 我的子视图设计为320宽度,但宽度
  2. 没有自己的约束
  3. 子视图已添加到单元格中,但水平约束如下所示:
    • @"|[_thirdPartyAnswerView]-38-|"
  4. 视图在添加到单元格后立即配置,这意味着titleLabel的文本已经设置好了。
  5. 无论出于何种原因,文字的布局就好像视图有完整的320而不是282。
  6. 标签永远不会更新,即使子视图的框架已更新为282,并且标签上有一些限制条件可以使其大小正确。
  7. 将xib中视图的大小更改为282解决了问题,因为标签的大小合适。
  8. 我仍然不明白为什么在父视图的大小同时具有前导和尾随约束时,标签不会重新布局。

    解决

    见Matt的答案如下:https://stackoverflow.com/a/15514707/287403

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

3 个答案:

答案 0 :(得分:42)

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

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

答案 1 :(得分:13)

我遇到了同样的问题,并使用this answer的建议解决了这个问题。在UILabel的子类中,我放置了以下代码:

- (void)layoutSubviews
{
    [super layoutSubviews];
    self.preferredMaxLayoutWidth = self.bounds.size.width;
}

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

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

答案 2 :(得分:0)

另外,检查是否在代码中将整数引用到布局约束中。

对我而言,经过一些计算(例如convertPoint:toView :)后,我传递了类似于23.99999997的内容,最终导致显示为单行的2行标签(尽管它的框架似乎是正确计算)。在我的例子中,CGRectIntegral做了这个伎俩!

舍入错误可能会导致你死亡:)