由于 NSLayoutConstraint 错误,具有 automaticDimension 的 UITableView 不会动态增加大小

Posted

技术标签:

【中文标题】由于 NSLayoutConstraint 错误,具有 automaticDimension 的 UITableView 不会动态增加大小【英文标题】:UITableView with automaticDimension doesn't dynamically increase size due to NSLayoutConstraint error 【发布时间】:2019-03-25 06:07:48 【问题描述】:

我正在创建一个自定义 UITableViewCell。目前,我只想让它在左侧有一个UIButton (checkButton),在按钮的右侧有两个UILabels(titleLabelnotesLabel)。

基本上,它应该看起来像一个标准的UITableViewCell,带有一个图像和两个文本标签(但请不要告诉我只是重复使用一个标准单元格,因为由于各种原因我不能这样做)。该按钮应具有固定大小 (16x16) 并在单元格中垂直居中。这两个标签应换行并展开以适合其内容。我正在尝试以编程方式定义此单元格,因此我创建了以下初始化程序来定义约束。

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) 
    super.init(style: style, reuseIdentifier: reuseIdentifier)
    titleLabel.translatesAutoresizingMaskIntoConstraints = false
    titleLabel.font = UIFont.systemFont(ofSize: 16)
    titleLabel.lineBreakMode = .byWordWrapping
    titleLabel.numberOfLines = 0
    contentView.addSubview(titleLabel)
    checkButton.translatesAutoresizingMaskIntoConstraints = false
    contentView.addSubview(checkButton)
    notesLabel.translatesAutoresizingMaskIntoConstraints = false
    notesLabel.font = UIFont.systemFont(ofSize: 13)
    notesLabel.lineBreakMode = .byWordWrapping
    notesLabel.numberOfLines = 0
    contentView.addSubview(notesLabel)
    addConstraint(NSLayoutConstraint(item: titleLabel,
                                     attribute: .top,
                                     relatedBy: .equal,
                                     toItem: contentView,
                                     attribute: .top,
                                     multiplier: 1,
                                     constant: 0))
    addConstraint(NSLayoutConstraint(item: notesLabel,
                                     attribute: .top,
                                     relatedBy: .equal,
                                     toItem: titleLabel,
                                     attribute: .bottom,
                                     multiplier: 1,
                                     constant: 0))
    addConstraint(NSLayoutConstraint(item: titleLabel,
                                     attribute: .trailing,
                                     relatedBy: .equal,
                                     toItem: contentView,
                                     attribute: .trailing,
                                     multiplier: 1,
                                     constant: -10))
    addConstraint(NSLayoutConstraint(item: notesLabel,
                                     attribute: .trailing,
                                     relatedBy: .equal,
                                     toItem: contentView,
                                     attribute: .trailing,
                                     multiplier: 1,
                                     constant: -10))
    addConstraint(NSLayoutConstraint(item: notesLabel,
                                     attribute: .bottom,
                                     relatedBy: .equal,
                                     toItem: contentView,
                                     attribute: .bottom,
                                     multiplier: 1,
                                     constant: 0))
    addConstraint(NSLayoutConstraint(item: checkButton,
                                     attribute: .leading,
                                     relatedBy: .equal,
                                     toItem: contentView,
                                     attribute: .leading,
                                     multiplier: 1,
                                     constant: 20))
    addConstraint(NSLayoutConstraint(item: checkButton,
                                     attribute: .centerY,
                                     relatedBy: .equal,
                                     toItem: contentView,
                                     attribute: .centerY,
                                     multiplier: 1,
                                     constant: 0))
    addConstraint(NSLayoutConstraint(item: checkButton,
                                     attribute: .height,
                                     relatedBy: .equal,
                                     toItem: nil,
                                     attribute: .notAnAttribute,
                                     multiplier: 0,
                                     constant: 16))
    addConstraint(NSLayoutConstraint(item: checkButton,
                                     attribute: .width,
                                     relatedBy: .equal,
                                     toItem: nil,
                                     attribute: .notAnAttribute,
                                     multiplier: 0,
                                     constant: 16))
    addConstraint(NSLayoutConstraint(item: notesLabel,
                                     attribute: .leading,
                                     relatedBy: .equal,
                                     toItem: checkButton,
                                     attribute: .trailing,
                                     multiplier: 1,
                                     constant: 12))
    addConstraint(NSLayoutConstraint(item: titleLabel,
                                     attribute: .leading,
                                     relatedBy: .equal,
                                     toItem: checkButton,
                                     attribute: .trailing,
                                     multiplier: 1,
                                     constant: 12))

当我运行这段代码时,它大部分都按预期工作,除了 Xcode 打印以下警告:[Warning] Warning once only: Detected a case where constraints ambiguously suggest a height of zero for a tableview cell's content view. We're considering the collapse unintentional and using standard height instead.

我通常会忽略这一点,但它似乎阻止了单元格扩展以适应其内容。例如,如果其中一个标签有足够的内容扩展到 3 行,则仅显示第一行。我想要的行为是让标签(以及扩展的单元格)扩展以适应它们的内容。我在高度限制方面做错了什么?

【问题讨论】:

约束应该添加到 contentView。 啊,谢谢!我现在可以停止拔头发了。如果您将其发布为答案,我会接受。 【参考方案1】:

注意:请在单元格中添加标签和按钮,而不是在单元格的 contentView 中。下面的代码可以工作,如果有任何问题,请检查并告诉我。

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) 
    super.init(style: style, reuseIdentifier: reuseIdentifier)

    addSubview(titleLabel)
    addSubview(checkButton)
    addSubview(notesLabel)

    titleLabel.translatesAutoresizingMaskIntoConstraints = false
    titleLabel.font = UIFont.systemFont(ofSize: 16)
    titleLabel.lineBreakMode = .byWordWrapping
    titleLabel.numberOfLines = 0

    checkButton.translatesAutoresizingMaskIntoConstraints = false

    notesLabel.translatesAutoresizingMaskIntoConstraints = false
    notesLabel.font = UIFont.systemFont(ofSize: 13)
    notesLabel.lineBreakMode = .byWordWrapping
    notesLabel.numberOfLines = 0

    addConstraint(NSLayoutConstraint(item: titleLabel,
                                     attribute: .top,
                                     relatedBy: .equal,
                                     toItem: self,
                                     attribute: .top,
                                     multiplier: 1,
                                     constant: 0))
    addConstraint(NSLayoutConstraint(item: notesLabel,
                                     attribute: .top,
                                     relatedBy: .equal,
                                     toItem: titleLabel,
                                     attribute: .bottom,
                                     multiplier: 1,
                                     constant: 0))
    addConstraint(NSLayoutConstraint(item: titleLabel,
                                     attribute: .trailing,
                                     relatedBy: .equal,
                                     toItem: self,
                                     attribute: .trailing,
                                     multiplier: 1,
                                     constant: -10))
    addConstraint(NSLayoutConstraint(item: notesLabel,
                                     attribute: .trailing,
                                     relatedBy: .equal,
                                     toItem: self,
                                     attribute: .trailing,
                                     multiplier: 1,
                                     constant: -10))
    addConstraint(NSLayoutConstraint(item: notesLabel,
                                     attribute: .bottom,
                                     relatedBy: .equal,
                                     toItem: self,
                                     attribute: .bottom,
                                     multiplier: 1,
                                     constant: 0))
    addConstraint(NSLayoutConstraint(item: checkButton,
                                     attribute: .leading,
                                     relatedBy: .equal,
                                     toItem: self,
                                     attribute: .leading,
                                     multiplier: 1,
                                     constant: 20))
    addConstraint(NSLayoutConstraint(item: checkButton,
                                     attribute: .centerY,
                                     relatedBy: .equal,
                                     toItem: self,
                                     attribute: .centerY,
                                     multiplier: 1,
                                     constant: 0))
    addConstraint(NSLayoutConstraint(item: checkButton,
                                     attribute: .height,
                                     relatedBy: .equal,
                                     toItem: nil,
                                     attribute: .notAnAttribute,
                                     multiplier: 0,
                                     constant: 16))
    addConstraint(NSLayoutConstraint(item: checkButton,
                                     attribute: .width,
                                     relatedBy: .equal,
                                     toItem: nil,
                                     attribute: .notAnAttribute,
                                     multiplier: 0,
                                     constant: 16))
    addConstraint(NSLayoutConstraint(item: notesLabel,
                                     attribute: .leading,
                                     relatedBy: .equal,
                                     toItem: checkButton,
                                     attribute: .trailing,
                                     multiplier: 1,
                                     constant: 12))
    addConstraint(NSLayoutConstraint(item: titleLabel,
                                     attribute: .leading,
                                     relatedBy: .equal,
                                     toItem: checkButton,
                                     attribute: .trailing,
                                     multiplier: 1,
                                     constant: 12))

【讨论】:

感谢您的回答!看起来这样可行,但我更喜欢 Sachin 的方法,因为添加到内容视图中可以在模式之间转换时提供漂亮的动画等。【参考方案2】:

这是我的方法

private func setUp() 
    self.selectionStyle = .none
    self.contentView.backgroundColor = .white

    let button = UIButton(frame: CGRect(x: 0, y: 0, width: 16, height: 16))
    button.setImage(UIImage(named: "arrow"), for: .normal)
    button.translatesAutoresizingMaskIntoConstraints = false
    self.contentView.addSubview(button)
    button.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 8.0).isActive = true
    button.centerYAnchor.constraint(equalTo: self.contentView.centerYAnchor).isActive = true
    button.widthAnchor.constraint(equalToConstant: button.frame.width).isActive = true
    button.heightAnchor.constraint(equalToConstant: button.frame.height).isActive = true

    let view = UIView()
    view.backgroundColor = .red
    view.translatesAutoresizingMaskIntoConstraints = false
    self.contentView.addSubview(view)
    view.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 8.0).isActive = true
    view.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: button.frame.width + 16.0).isActive = true
    view.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: 8.0).isActive = true
    view.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: -8.0).isActive = true

    titleLabel.translatesAutoresizingMaskIntoConstraints = false
    titleLabel.numberOfLines = 0
    view.addSubview(titleLabel)
    titleLabel.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
    titleLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
    titleLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true

    notesLabel.translatesAutoresizingMaskIntoConstraints = false
    notesLabel.numberOfLines = 0
    view.addSubview(notesLabel)
    notesLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 8.0).isActive = true
    notesLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
    notesLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
    notesLabel.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true

如果标签是多行动态高度,使用以下来启用自调整单元格

tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 44.0

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat 
    return UITableView.automaticDimension

结果

【讨论】:

这很好,除了我会为估计的行高提供一个实际值。主要是滚动指示器不会在您滚动时闪烁和调整大小。此外,提供一个具体的值更有意义,说估计一个单元格的估计值并没有真正增加价值。

以上是关于由于 NSLayoutConstraint 错误,具有 automaticDimension 的 UITableView 不会动态增加大小的主要内容,如果未能解决你的问题,请参考以下文章

错误的 NSLayoutConstraint 导致黑屏

调整 UITableViewCell 大小的 NSLayoutConstraint 错误

错误:NSInvalidUnarchiveOperationException:无法实例化名为 NSLayoutConstraint 的类

如何修复似乎不影响自定义 UITableViewCell 布局的奇怪 NSLayoutConstraint 错误

暂时移除 NSLayoutConstraint?

NSLayoutConstraint constraintWithItem... 排序