如何为具有动态高度的表格单元格正确设置自动布局?

Posted

技术标签:

【中文标题】如何为具有动态高度的表格单元格正确设置自动布局?【英文标题】:How to set auto layout correctly for a table cell with dynamic height? 【发布时间】:2017-12-22 07:31:02 【问题描述】:

我尝试创建一个UITableViewCell 并设置子视图如下:

class MyCellView: UITableViewCell 

    private let ImageView = UIImageView()
    private let titleView = UILabel()

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) 

        super.init(style: style, reuseIdentifier: reuseIdentifier)

        contentView.addSubview(ImageView)
        contentView.addSubview(titleView)

        ImageView.translatesAutoresizingMaskIntoConstraints = false
        ImageView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
        ImageView.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
        ImageView.rightAnchor.constraint(equalTo: contentView.rightAnchor).isActive = true
        ImageView.heightAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.6).isActive = true

        titleView.translatesAutoresizingMaskIntoConstraints = false
        titleView.topAnchor.constraint(equalTo: ImageView.bottomAnchor).isActive = true
        titleView.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
        titleView.rightAnchor.constraint(equalTo: contentView.rightAnchor).isActive = true
        titleView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true

        titleView.numberOfLines = 0

        titleView.text = " "   // <-- *** please note this line

    

在表格视图中,我已将其设置为使用动态高度:

tableView.estimatedRowHeight = 80
tableView.rowHeight = UITableViewAutomaticDimension

我的问题是,如果我没有在单元格中设置titleView 的文本,例如我在单元格视图中备注最后一行:

// titleView.text = " "

我会从 XCode 中得到约束错误。但如果我设置文本,一切正常。

文本是从 Internet 动态下载的,因此文本可能为空。另外我可以把文本设置为“”,一个空格,我想知道我在这里有什么误解吗?如果我更正某些内容,文本可以为空(“”或 nil)吗?

同样,如果我完全删除有关titleView 的代码,我只想在单元格中有一个UIImageView,我发现单元格的高度没有扩大。如何设置它以便我可以在单元格中获得动态高度的图像?

编辑:约束错误:

2017-12-22 15:35:34.715865+0800 MyProject[15774:733515] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x60c000095450 V:|-(0)-[UIImageView:0x7ff40df0de90]   (active, names: '|':UITableViewCellContentView:0x7ff40df0edc0 )>",
"<NSLayoutConstraint:0x60c0000955e0 UIImageView:0x7ff40df0de90.height == 0.6*UITableViewCellContentView:0x7ff40df0edc0.width   (active)>",
"<NSLayoutConstraint:0x60c000095630 V:[UIImageView:0x7ff40df0de90]-(0)-[UILabel:0x7ff40df0e0c0]   (active)>",
"<NSLayoutConstraint:0x60c0000957c0 UILabel:0x7ff40df0e0c0.bottom == UITableViewCellContentView:0x7ff40df0edc0.bottom   (active)>",
"<NSLayoutConstraint:0x60c000096210 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x7ff40df0edc0.height == 248.333   (active)>",
"<NSLayoutConstraint:0x60c000095ea0 'UIView-Encapsulated-Layout-Width' UITableViewCellContentView:0x7ff40df0edc0.width == 414   (active)>"
)

Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x60c000095630 V:[UIImageView:0x7ff40df0de90]-(0)-[UILabel:0x7ff40df0e0c0]   (active)>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

【问题讨论】:

什么是约束错误? @MilanNosáľ 已更新。 【参考方案1】:

如果你想避免警告,试试下面的代码

class MyCellView: UITableViewCell 

  private let ImageView = UIImageView()
  private let titleView = UILabel()

  override init(style: UITableViewCellStyle, reuseIdentifier: String?) 

    super.init(style: style, reuseIdentifier: reuseIdentifier)

    contentView.addSubview(ImageView)
    contentView.addSubview(titleView)

    ImageView.translatesAutoresizingMaskIntoConstraints = false
    ImageView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
    ImageView.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
    ImageView.rightAnchor.constraint(equalTo: contentView.rightAnchor).isActive = true
    let heightConstraint = ImageView.heightAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.6)
    heightConstraint.priority = UILayoutPriority.defaultLow
    heightConstraint.isActive = true

    titleView.translatesAutoresizingMaskIntoConstraints = false
    titleView.topAnchor.constraint(equalTo: ImageView.bottomAnchor).isActive = true
    titleView.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
    titleView.rightAnchor.constraint(equalTo: contentView.rightAnchor).isActive = true
    titleView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true

    titleView.numberOfLines = 0

//    titleView.text = " "
  

如果您完全删除有关titleView 的代码并希望在单元格中拥有一个具有动态高度的UIImageView,请为imageView 添加bottomAnchor 约束

class MyCellView: UITableViewCell 

  private let ImageView = UIImageView()
  private let titleView = UILabel()

  override init(style: UITableViewCellStyle, reuseIdentifier: String?) 

    super.init(style: style, reuseIdentifier: reuseIdentifier)

    contentView.addSubview(ImageView)
    contentView.addSubview(titleView)

    ImageView.translatesAutoresizingMaskIntoConstraints = false
    ImageView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
    ImageView.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
    ImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
    ImageView.rightAnchor.constraint(equalTo: contentView.rightAnchor).isActive = true
    let heightConstraint = ImageView.heightAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.6)
    heightConstraint.priority = UILayoutPriority.defaultLow
    heightConstraint.isActive = true
  

【讨论】:

我按照你的建议添加了这行代码,仍然有约束警告。 @JoeHuang 我已经更新了我的答案,你可以再次查看。【参考方案2】:

这似乎是一个已知“错误”的结果 - tableView 设置的高度与自动布局计算的高度发生冲突。渲染单元格时,tableView 首先应用默认高度,计算自动布局高度,然后使用后者 - 至少看起来如此。见我的question。

这意味着您使用的约束是可以的(至少在我看来是这样)。只需将定义高度的约束之一设置为priority = 999(这样在停用默认高度约束之前不会导致任何冲突)。最终,无论如何都会导致使用你的约束,所以不会造成任何麻烦。

P.S.:注意 imageView 的高度限制:

ImageView.heightAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.6).isActive = true

如果您想省略图像(因此将其渲染为高度 = 0),那也会给您带来麻烦(同样,降低优先级可能会让您到达您想要的位置)。如果图像总是在那里,那就别管我的 P.S. :)。

【讨论】:

以上是关于如何为具有动态高度的表格单元格正确设置自动布局?的主要内容,如果未能解决你的问题,请参考以下文章

滚动时 UITableViewCell 自动布局抖动的动态单元格高度问题

具有动态大小的单元格的复杂自动布局

具有自动布局的自定义单元格的动态高度

自动布局:具有可变高度和两个标签的表格单元格

如何为具有与内置单元格相同的布局指标的“UITableView”提供自定义“UITableCell”?

界面生成器中的自动布局高度 >= 0 帧