iOS 8中的自动调整UITableViewCell大小

8

我有一个包含多行标签的UITableViewCell子类,我希望该单元格根据标签内容动态调整大小。我知道iOS 8引入了基于AutoLayout约束的自动调整单元格大小功能,并且在SO上已经找到了几个示例,但我仍然无法正确实现此行为。

这是我的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)]];
}

在表视图控制器中,我将行高设置为UITableViewAutomaticDimension(并设置了预估行高)。在运行时,我遇到了一系列自动布局错误,所有的表视图单元格几乎完全重叠。
自动布局冲突出现在以下约束之间:
  • V:|-(10)-[_nameLabel]
  • V:[_nameLabel(20)]
  • V:[_nameLabel]-(10)-[_tweetLabel]
  • V:[_tweetLabel]-(10)-|
  • V:[cell(44)]
我怀疑最后一个约束“UIView-Encapsulated-Layout-Height”会强制高度为44,可能是问题的原因。但我不确定它来自哪里,希望有人能解决这个问题。

2
дҪ жңүжЈҖжҹҘиҝҮдҪ зҡ„UITableViewCellдёҠзҡ„translatesAutorezisingMaskIntoConstraintsжҳҜеҗҰи®ҫзҪ®дёәNOеҗ—пјҹ - kljhanson
@kljhanson 刚刚尝试了一下,感谢您的建议,但现在一个大灰色矩形占据了屏幕的大部分空间(Xcode的视图检查显示它是数百个单元格分隔符)。我在委托方法中肯定给出了正确的单元格计数,有什么想法为什么会发生这种情况? - futurevilla216
不确定这是否是苹果自动布局的错误,但此帖子中的用户建议是:https://dev59.com/618f5IYBdhLWcg3wFfP_。我也遇到了相同的问题(44),我的动态tableviewcell高度约束冲突。它似乎只在iOS 8中出现,而iOS 7可以正常工作。奇怪的是,如果您的所有单元格高度相同并且不进行动态单元格高度,则不会出现此问题。我真的希望这是苹果的错误 >.<(注意:我正在使用iOS 8之前的动态单元格高度代码) - Zhang
3个回答

10
为了实现表视图单元格的自动行高度,你需要按照以下步骤进行操作:
  1. 在单元格的contentView中实现自动布局约束,允许视图表达其首选高度。请确保将 UILabel 设置为多行换行方式。

    一定要在两个维度上定义轴线约束,即,约束应该从一个边缘收集到另一个边缘。也许最简单的方法是将你的自定义内容实现为普通的 UIView (易于测试),然后使用约束使 UITableViewCell.contentView 紧贴着该视图。(我使用这个代码片段 来自动构建“包装视图的单元格”)。

  2. 设置 tableView.rowHeight = UITableViewAutomaticDimension

  3. 设置 tableView.estimatedRowHeight = 400 或其他合理大的值,以解决UIKit在估算值过低时可能出现的问题。

我花了很多时间来学习和运用这个功能。 这个github仓库 展示了七个完整的自动行高度表视图单元格示例,包含一个包装文本的单独标签 -- 程序化、基于nib、基于storyboard等。

最后,如果在表视图加载时看到警告或类似 "UIView-Encapsulated-Layout-Height" 的不可满足约束,请不用太担心。这是 UITableView 初始创建单元格时的一个副作用,它根据自动布局约束确定单元格的大小,并使 UITableViewCell 紧密包裹着其 contentView。上述提到的仓库有更详细的讨论和代码,可以探索此 API 的某些令人尴尬的角落。

在加载单元格并滚动一段时间后,如果仍然持续出现约束违规警告,或者最初看到的布局不正确,则应该关注这些警告。在这种情况下,再次强调,第一步应始终确保您的约束是正确的,在可能的情况下通过在普通的 UIView 中进行开发和测试来实现。


2
谢谢!我将 tableView.estimatedRowHeight 更改为较大的值,一切都正常工作了 :) - Shyngys Kassymov

4
我刚刚遇到了这个问题。
从其他 Stackoverflow 帖子中,他们推荐:
self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

一开始这对我没用。我发现我还需要做以下事情:

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

我的自定义单元格的初始化方法如下:

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

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

    return self;
}

我把代码放在我的“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);
}

问题已经解决,我的手机也看起来正确了。
这对你有用吗?

谢谢!在我的情况下,视图看起来还好,但是不知何故我收到了很多警告。我按照您的建议添加了遮罩和框架。我想问题在于默认高度为44对于我的视图来说太小了,即使高度在显示时会拉伸,内部布局管理器仍然会抱怨。我需要至少200的高度,因此我设置了一个安全框架(0,0,w,200)。 - Ferran Maylinch

2

您确定在单元格上已将-translatesAutoresizingMaskIntoConstraints设置为NO吗?如果没有,系统会根据自动调整大小掩码生成约束条件,这是在iOS上进行布局的以前方式,并且在使用自动布局时应禁用。


谢谢你指出这个问题,我确实为单元格的子视图设置了这个属性,但没有为单元格本身设置。 - futurevilla216
但是当我将其设置在单元格上时,有趣的事情发生了 - 一个灰色矩形现在占据了屏幕的大部分,并且根据Xcode的视图检查,它是数百个表视图分隔符,你有什么想法是什么原因?我在委托回调中返回了正确的行数,所以不确定可能是什么原因。 - futurevilla216
不知道这是否有所帮助,但是苹果公司关于“updateConstraints”的文档明确指出,在实现过程中应该将“[super updateConstraints]”作为最后一步调用。 - wander

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接