【问题标题】:Custom TableViewCell triggers NSLayoutConstraint error 'UIView-Encapsulated-Layout-Height'自定义 TableViewCell 触发 NSLayoutConstraint 错误 'UIView-Encapsulated-Layout-Height'
【发布时间】:2017-02-01 01:10:41
【问题描述】:

我对单元格使用自动高度。 每次我想用新数据(这里的模型变量)更新我的单元格时,我都会更新我的自动布局约束并且我得到一个错误。 这里只是为了说明问题,我什至没有更改约束。 我只是要求重新计算布局。

在单元格的第一次初始化时:没有警告,没有问题。

错误:

<NSLayoutConstraint:0x174285d70 'UIView-Encapsulated-Layout-Height'
 UITableViewCellContentView:0x1030f8a50.height == 500   (active)>

代码:

tableView.rowHeight = UITableViewAutomaticDimension

override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
      return 500
  }

class TestTableViewCell: UITableViewCell {

  var model: X? {
    didSet {
      setNeedsLayout()
      layoutIfNeeded()
    }
  }

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

  override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)

    let viewtop = UIView()
    let viewbottom = UIView()

    viewtop.backgroundColor = UIColor.yellow
    viewbottom.backgroundColor = UIColor.red
    contentView.backgroundColor = UIColor.blue

    contentView.addSubview(viewtop)
    contentView.addSubview(viewbottom)

    viewtop.snp.makeConstraints { (make) in
      make.top.equalTo(contentView)
      make.left.right.equalTo(contentView)
      make.height.equalTo(50)
    }

    viewbottom.snp.makeConstraints { (make) in
      make.top.equalTo(viewtop.snp.bottom)
      make.left.right.equalTo(contentView)
      make.bottom.equalTo(contentView)
      make.height.equalTo(120)
    }
  }

  required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }

}

问题: 为什么在要求重新布局相同约束后,我会收到错误消息?

编辑:这里是另一个更好理解的例子。

var botViewHeightConstraint:Constraint!
class TestTableViewCell: UITableViewCell {

  var model: Int? {
    didSet {
      if model == 1 {
        botViewHeightConstraint.update(offset:200)
      }else{
        botViewHeightConstraint.update(offset:120)
      }
    }
  }

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

  override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)

    let viewtop = UIView()
    let viewbottom = UIView()

    viewtop.backgroundColor = UIColor.yellow
    viewbottom.backgroundColor = UIColor.red
    contentView.backgroundColor = UIColor.blue

    contentView.addSubview(viewtop)
    contentView.addSubview(viewbottom)

    viewtop.snp.makeConstraints { (make) in
      make.top.equalTo(contentView)
      make.left.right.equalTo(contentView)
      make.height.equalTo(50)
    }

    viewbottom.snp.makeConstraints { (make) in
      make.top.equalTo(viewtop.snp.bottom)
      make.left.right.equalTo(contentView)
      make.bottom.equalTo(contentView)
      botViewHeightConstraint = make.height.equalTo(120).constraint
    }
  }

  required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }

}

CellForRow 代码:

 override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    if let post = fetchedResultsController?.fetchedObjects?[(indexPath as NSIndexPath).section]  {
       let cell = tableView.dequeueReusableCell(withIdentifier: imagePostCellId) as! TestTableViewCell!
       cell.model = 1
       return cell
    }
}

【问题讨论】:

  • 您可以将我的自定义单元格复制粘贴到测试项目中,您将看到警告。我正在通过 cocoapods 将 Snapkit 用于 swift 3。

标签: ios swift uitableview autolayout snapkit


【解决方案1】:

首先,您一直忽略将子视图的translatesAutoresizingMaskIntoConstraints 设置为false。这很重要。 (不过,也许 snapkit 会为你做到这一点。我不知道。)

第二——这是重点——你所做的不是如何从内向外调整高度可变的单元格。您不会像您的代码那样更改绝对高度约束。大小最终基于子视图的内在内容大小。当然,某些子视图可能具有绝对高度,但最终必须至少有一个具有固有内容大小。 那是您可以动态更改的大小,以确定单元格的高度。

这就是为什么,例如,包含 UILabel 的单元格如此易于与动态行高一起使用的原因。它具有固有的内容大小。

内在内容大小与单元格的内置高度(控制台转储中的 UIView-Encapsulated-Layout-Height)不冲突;它补充它(当运行时在幕后调用systemLayoutSizeFitting(UILayoutFittingCompressedSize),这就是自动可变行高的工作原理)。

如果您使用具有intrinsicContentSize 实现的自定义子视图,并且如果在单元格中设置模型值会触发对invalidateIntrinsicContentSize 的调用,那么您的示例将在控制台中完美运行而不会出现任何投诉。

以下是此类自定义视图的示例:

class MyView : UIView {
    var h : CGFloat = 200 {
        didSet {
            self.invalidateIntrinsicContentSize()
        }
    }
    override var intrinsicContentSize: CGSize {
        return CGSize(width:300, height:self.h)
    }
}

当这是单元格内容视图的子视图时,将此视图的 h 设置为 cellForRow 会正确调整单元格的高度。

例如,假设我们单元格的内容视图只有一个子视图v,它是一个MyView。那么:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! MyCell
    let even = indexPath.row % 2 == 0
    cell.v.backgroundColor = even ? .red : .green
    cell.v.h = even ? 40 : 80 // triggers layout!
    return cell
}

【讨论】:

  • 根据您的要求,我也将其发布为可下载的示例项目:github.com/mattneub/variableRowHeights 下载示例并在 Xcode 8 中运行它。疯狂滚动!没问题。细胞高低交替。
  • 首先,您一直忽略将子视图的 translatesAutoresizingMaskIntoConstraints 设置为 false。这很重要。 (不过,也许 snapkit 会为你做这件事。我不知道。) -> 是的,Snapkit 会为我做这件事
  • 对不起,马特,我的错,我虽然接受你的回答,你会自动获得赏金......
【解决方案2】:

当你已经设置了相对于内容和顶视图的上下左右约束时,为什么还要添加“make.height.equalTo(120)”

根据您的代码,单元格高度似乎始终为 50+120。

还要查看覆盖 heightForRow 和返回 UITableViewAutomaticDimension 是否有效。

【讨论】:

  • 我已经在 heightForRow 中设置了 UITableViewAutomaticDimension。我添加了大小 + 底部 + 顶部,因为这是使用自动布局调整单元格大小所必需的。
  • UITableViewAutomaticDimension 如果用于动态布局 wrt 约束。
  • 如果你的单元格总是一样的高度,你为什么要在这里使用 UITableViewAutomaticDimension。当您已经知道要达到的高度时,它将不必要地进行布局传递。只需传递恒定的高度
  • 原因是单元格的高度并不总是相同的。在示例中,我设置了固定值,例如 if model == 1 { botViewHeightConstraint.update(offset:200) } 但在我的项目中,单元格高度实际上取决于 textContent 和图像高度。
猜你喜欢
  • 2021-09-01
  • 1970-01-01
  • 2015-04-09
  • 1970-01-01
  • 2015-10-09
  • 2016-03-04
  • 2015-06-16
  • 2021-05-21
  • 2014-09-23
相关资源
最近更新 更多