在iOS 8中自动调整UITableViewCell的大小

时间:2022-03-13 08:17:36

I have a UITableViewCell subclass which contains a multiline label, and I would like the cell to size itself dynamically based on the content of that label. I'm aware that iOS 8 introduced auto-sizing cells based on AutoLayout constraints, and I've found several examples of this already on SO, but I'm still having some trouble implementing this behavior properly.

我有一个包含多行标签的UITableViewCell子类,我希望单元格根据该标签的内容动态调整大小。我知道iOS 8引入了基于AutoLayout约束的自动调整大小的单元格,我已经在SO上找到了几个这样的例子,但我仍然在正确实现这种行为方面遇到一些麻烦。

Here's my updateConstraints implementation:

这是我的updateConstraints实现:

- (void)updateConstraints {
    [super updateConstraints];

    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-10-[_nameLabel(==20)]-10-[_tweetLabel]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_nameLabel, _tweetLabel)]];
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[_avatarView]-10-[_nameLabel]-10-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_avatarView, _nameLabel)]];

    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_nameLabel]-10-[_tweetLabel]-10-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_nameLabel, _tweetLabel)]];
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[_avatarView]-10-[_tweetLabel]-10-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_avatarView, _tweetLabel)]];

    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-10-[_avatarView(==45)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_avatarView)]];
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-10-[_avatarView(==45)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_avatarView)]];
}

In the table view controller I set the row height to UITableViewAutomaticDimension (and I set an estimated row height as well). At runtime, I get a series of auto layout errors and all of the table view cells appear nearly completely overlapped.

在表视图控制器中,我将行高设置为UITableViewAutomaticDimension(我也设置了估计的行高)。在运行时,我得到一系列自动布局错误,所有表视图单元格几乎完全重叠。

The auto layout conflicts are between the following constraints:

自动布局冲突在以下约束之间:

  • V:|-(10)-[_nameLabel]
  • V:[_nameLabel(20)]
  • V:[_nameLabel]-(10)-[_tweetLabel]
  • V:[_tweetLabel]-(10)-|
  • V:[cell(44)]

I suspect the last constraint, "UIView-Encapsulated-Layout-Height", which forces a height of 44, is the cause of the issue, but I'm not quite sure where that comes from, so hopefully somebody can shed some light on the issue.

我怀疑最后一个约束,“UIView-Encapsulated-Layout-Height”,强制高度为44,是问题的原因,但是我不太确定它来自哪里,所以希望有人可以解释一下问题。

3 个解决方案

#1


10  

In order to implement automatic row heights for table view cells, you need to do the following:

要为表视图单元格实现自动行高,您需要执行以下操作:

  1. Implement Auto Layout constraints within the cell's contentView that allow the view to express its preferred height. Be sure to set UILabels to word wrap over multiple lines.

    在单元格的contentView中实现自动布局约束,允许视图表达其首选高度。务必将UILabels设置为多行自动换行。

    Be sure you've defined an axial chain of constraints in both dimensions, that is, constraints that collectively bind all the way from one edge of the view to the other. Perhaps the easiest way to be sure these constraints are correct is to implement your custom content as a plain old UIView (which is easy to test), and then use constraints so that the UITableViewCell.contentView hugs that view. (I use this gist to automate building the "view-wrapping cell".)

    确保您已在两个维度中定义了约束的轴向链,即从视图的一个边缘到另一个边缘共同绑定的约束。也许确保这些约束正确的最简单方法是将自定义内容实现为普通的旧UIView(易于测试),然后使用约束以便UITableViewCell.contentView拥抱该视图。 (我使用这个要点自动构建“视图包装单元”。)

  2. Set tableView.rowHeight = UITableViewAutomaticDimension

    设置tableView.rowHeight = UITableViewAutomaticDimension

  3. Set tableView.estimatedRowHeight = 400 or some other reasonably generous value, in order to workaround some UIKit bugs when the estimate is too low.
  4. 设置tableView.estimatedRowHeight = 400或其他一些相当大的值,以便在估计值太低时解决一些UIKit错误。

I have spent a puzzling amount of time working with this feature. This github repo shows seven complete examples of self-sizing table view cells containing a single label of wrapping text -- programmatic, nib-based, storyboard-based, etc..

我花了不少时间处理这个功能。这个github repo显示了七个完整的自调整表格视图单元格的示例,其中包含一个包装文本的标签 - 程序化,基于笔尖,基于故事板等。

Finally, do not worry too much if you see warnings about unsatisfiable constraints mentioning "UIView-Encapsulated-Layout-Height" or similar at the first time the table view loads. This is an artefact of UITableView's initial process for creating a cell, determining what its size should be based on Auto Layout Constraints, and keeping the UITableViewCell tightly wrapping its contentView. The repo I mentioned above has more extensive discussion and code for exploring this somewhat awkward corner of the API.

最后,如果您在第一次加载表视图时看到关于提及“UIView-Encapsulated-Layout-Height”或类似的不可满足约束的警告,请不要太担心。这是UITableView创建单元格的初始过程,确定其大小应基于自动布局约束,并保持UITableViewCell紧密包装其contentView的人工制品。我上面提到的repo有更广泛的讨论和代码,用于探索API的这个有点尴尬的角落。

You should only worry about constraint-violation warnings if they persist even after the cell has loaded and has scrolled a bit, or if you are seeing incorrect layouts initially. In this case, again, the first step should always be to ensure your constraints are correct by developing them and testing them in isolation if possible, in a plain UIView.

如果它们在单元格已加载并且滚动了一点之后仍然存在,或者如果您最初看到不正确的布局,则应该只担心约束违规警告。在这种情况下,第一步应该始终是通过开发它们并在可能的情况下单独测试它们来确保您的约束是正确的,在简单的UIView中。

#2


4  

I just came across this issue.

我刚刚遇到过这个问题。

From numerous other * post they recommend:

从众多其他*帖子中,他们建议:

self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

That didn't work for me at first. I found that I also need to do:

起初这对我不起作用。我发现我还需要这样做:

self.frame = CGRectMake(0, 0, self.frame.size.width, 50);

My custom cell's init method looks like his:

我的自定义单元格的init方法看起来像他的:

-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];

    if(self)
    {
        [self initViews];
        [self initConstraints];
    }

    return self;
}

I put the code in my "initViews" method:

我把代码放在我的“initViews”方法中:

-(void)initViews
{
    ...

    // fixes an iOS 8 issue with UIViewEncapsulated height 44 bug
    self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
    self.frame = CGRectMake(0, 0, self.frame.size.width, 50);
}

The problem went away and my cell looks correct too.

问题消失了,我的细胞看起来也是正确的。

Does this work for you?

这对你有用吗?

#3


2  

Are you sure you have -translatesAutoresizingMaskIntoConstraints set to NO on the cell? If you don't, the system generates constraints based on the autoresizing mask, which was the previous way of doing layout on iOS, and should be disabled when using Auto Layout.

您确定在单元格上将-translatesAutoresizingMaskIntoConstraints设置为NO吗?如果不这样做,系统会根据自动调整遮罩生成约束,这是以前在iOS上进行布局的方式,在使用自动布局时应该禁用。

#1


10  

In order to implement automatic row heights for table view cells, you need to do the following:

要为表视图单元格实现自动行高,您需要执行以下操作:

  1. Implement Auto Layout constraints within the cell's contentView that allow the view to express its preferred height. Be sure to set UILabels to word wrap over multiple lines.

    在单元格的contentView中实现自动布局约束,允许视图表达其首选高度。务必将UILabels设置为多行自动换行。

    Be sure you've defined an axial chain of constraints in both dimensions, that is, constraints that collectively bind all the way from one edge of the view to the other. Perhaps the easiest way to be sure these constraints are correct is to implement your custom content as a plain old UIView (which is easy to test), and then use constraints so that the UITableViewCell.contentView hugs that view. (I use this gist to automate building the "view-wrapping cell".)

    确保您已在两个维度中定义了约束的轴向链,即从视图的一个边缘到另一个边缘共同绑定的约束。也许确保这些约束正确的最简单方法是将自定义内容实现为普通的旧UIView(易于测试),然后使用约束以便UITableViewCell.contentView拥抱该视图。 (我使用这个要点自动构建“视图包装单元”。)

  2. Set tableView.rowHeight = UITableViewAutomaticDimension

    设置tableView.rowHeight = UITableViewAutomaticDimension

  3. Set tableView.estimatedRowHeight = 400 or some other reasonably generous value, in order to workaround some UIKit bugs when the estimate is too low.
  4. 设置tableView.estimatedRowHeight = 400或其他一些相当大的值,以便在估计值太低时解决一些UIKit错误。

I have spent a puzzling amount of time working with this feature. This github repo shows seven complete examples of self-sizing table view cells containing a single label of wrapping text -- programmatic, nib-based, storyboard-based, etc..

我花了不少时间处理这个功能。这个github repo显示了七个完整的自调整表格视图单元格的示例,其中包含一个包装文本的标签 - 程序化,基于笔尖,基于故事板等。

Finally, do not worry too much if you see warnings about unsatisfiable constraints mentioning "UIView-Encapsulated-Layout-Height" or similar at the first time the table view loads. This is an artefact of UITableView's initial process for creating a cell, determining what its size should be based on Auto Layout Constraints, and keeping the UITableViewCell tightly wrapping its contentView. The repo I mentioned above has more extensive discussion and code for exploring this somewhat awkward corner of the API.

最后,如果您在第一次加载表视图时看到关于提及“UIView-Encapsulated-Layout-Height”或类似的不可满足约束的警告,请不要太担心。这是UITableView创建单元格的初始过程,确定其大小应基于自动布局约束,并保持UITableViewCell紧密包装其contentView的人工制品。我上面提到的repo有更广泛的讨论和代码,用于探索API的这个有点尴尬的角落。

You should only worry about constraint-violation warnings if they persist even after the cell has loaded and has scrolled a bit, or if you are seeing incorrect layouts initially. In this case, again, the first step should always be to ensure your constraints are correct by developing them and testing them in isolation if possible, in a plain UIView.

如果它们在单元格已加载并且滚动了一点之后仍然存在,或者如果您最初看到不正确的布局,则应该只担心约束违规警告。在这种情况下,第一步应该始终是通过开发它们并在可能的情况下单独测试它们来确保您的约束是正确的,在简单的UIView中。

#2


4  

I just came across this issue.

我刚刚遇到过这个问题。

From numerous other * post they recommend:

从众多其他*帖子中,他们建议:

self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

That didn't work for me at first. I found that I also need to do:

起初这对我不起作用。我发现我还需要这样做:

self.frame = CGRectMake(0, 0, self.frame.size.width, 50);

My custom cell's init method looks like his:

我的自定义单元格的init方法看起来像他的:

-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];

    if(self)
    {
        [self initViews];
        [self initConstraints];
    }

    return self;
}

I put the code in my "initViews" method:

我把代码放在我的“initViews”方法中:

-(void)initViews
{
    ...

    // fixes an iOS 8 issue with UIViewEncapsulated height 44 bug
    self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
    self.frame = CGRectMake(0, 0, self.frame.size.width, 50);
}

The problem went away and my cell looks correct too.

问题消失了,我的细胞看起来也是正确的。

Does this work for you?

这对你有用吗?

#3


2  

Are you sure you have -translatesAutoresizingMaskIntoConstraints set to NO on the cell? If you don't, the system generates constraints based on the autoresizing mask, which was the previous way of doing layout on iOS, and should be disabled when using Auto Layout.

您确定在单元格上将-translatesAutoresizingMaskIntoConstraints设置为NO吗?如果不这样做,系统会根据自动调整遮罩生成约束,这是以前在iOS上进行布局的方式,在使用自动布局时应该禁用。