UITableViewCell 通过内容拥抱自动调整大小

UITableViewCell autosizing with content hugging

我想要实现的是在其中包含 UILabel 的情况下自动调整单元格大小。 在我开始拥抱部分之前,我能够让它发挥作用。 通过拥抱我的意思是如果文本真的很短,让 UILabel 尽可能小。

我准备了一个非常简单的问题示例:

http://s000.tinyupload.com/index.php?file_id=11087347944522931899

在我将尾随约束设置为“500”优先级(低于 hugging,即 750)之后,一切都很好。 现在看来,调整单元格正在减小 UILabel 的尺寸,但从未增加,所有高度计算都是错误的。

在第一个真正短的单元格进来之后 - 在这个单元格坏掉之后:

通过调试我发现一旦 UILabel 达到 8px,它就会变大,所有高度计算都是针对 8px 宽度完成的。

我找到了解决方法,并在单元格因任何原因出队后重置 preferredWidth 有帮助,但我不确定是否有更好的方法来解决该问题。

我的解决方法是在附加项目(第 55 行、第 81 行)中添加注释ChatTableController.m,我正在为此寻找合适的解决方案。

我建议做的是通过测量文本边界来预先计算单元格高度。最简单也是性能最差的方法是使用 - (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(NSDictionary *)attributes context:(NSStringDrawingContext *)context

理想情况下,您希望尽快计算边界。 CoreText 将帮助您解决这个问题。

使用 CoreText 预计算文本 bounds/layout 的一个很好的例子是 this GitHub repo。

如果您需要更多 info/help,请告诉我。

首先,将标签的尾随约束设置为 >=20。这让标签收缩。

你真正的问题是这个方法:

- (void)setBounds:(CGRect)bounds {
    [super setBounds:bounds];

    if (self.numberOfLines == 0 && bounds.size.width != self.preferredMaxLayoutWidth) {
        self.preferredMaxLayoutWidth = self.bounds.size.width;
        [self setNeedsUpdateConstraints];
    }
}

heightForRowAtIndexPathcellForRowAtIndexPath中创建单元格时最好设置cell.theLabel.preferedMaxLayoutWidth

如果您完全注释掉这段代码,它似乎一切正常。

如果您在每次调用 heightForRowAtIndexPath 时创建一个新的原型单元格,您也可以使您的代码正常工作。这就是导致我使用上述功能的原因。

似乎每次调用heightForRowAtIndexPath时,原型单元格的重用导致高度的组合增加。如果您向下滚动并返回,就会看到这个。

所以我会完全删除此方法,让约束为您完成工作。我看到当一行达到精确大小而其他所有内容紧凑时,单元格会缩小。

在 createCellForRowAtIndexPath 中设置文本之前,添加以下内容以使布局宽度全屏减去 20 个边距:

cell.messageLabel.preferredMaxLayoutWidth=self.view.bounds.size.width-40;

在 heightForRowAtIndexPath 的原型单元格中设置文本之前:

prototypeCell.messageLabel.preferredMaxLayoutWidth=self.view.bounds.size.width-40;

设置preferredMaxLayoutWidth会导致旋转出现问题。每个单元格设置这个意味着每个单元格现在都有特定于方向的约束。为了解决这个问题,您必须通过将以下内容添加到视图控制器来重新加载 table 旋转:

- (void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [self.tableView reloadData];
}

最终结果: