UITableViewCell高度基于行内容使用自动布局动态计算

时间:2018-11-17 15:58:50

标签: ios uitableview autolayout uikit

我已经在nib文件中准备了UITableViewCell,并具有从上到下的所有约束。在表格视图中计算行高似乎可行。

但是,如果我想基于某些行数据条件激活/停用单元格中的其他约束,那么我会遇到问题,因为似乎未计算高度。问题是什么?在下面的示例中,TableRowDM.hidden表示字幕是否应该隐藏。我通过激活未激活的约束(常数为0的高度)并将titleLabel和subtitleLabel之间的垂直间距设置为0,来隐藏此UILabel。

struct TableRowDM {
    let title: String
    let subTitle: String
    let hidden: Bool
}

let cellId: String = "CellView"

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var tableView: UITableView!

    let rowData: [TableRowDM] = [
        TableRowDM(title: "Row 1", subTitle: "Short title 1th row", hidden: false),
        TableRowDM(title: "Row 2", subTitle: "Short title 2th row", hidden: true),
        TableRowDM(title: "Row 3", subTitle: "Very long text in subtitle at 3th row to test text wrapping and growing subtitle height", hidden: false),
        TableRowDM(title: "Row 4", subTitle: "Long text in subtitle at 4th row", hidden: false),
        TableRowDM(title: "Row 5", subTitle: "Long text in subtitle at 5th row", hidden: true),
        TableRowDM(title: "Row 6", subTitle: "Long text in subtitle at 6th row", hidden: false),
    ]

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.rowHeight = UITableViewAutomaticDimension
        tableView.estimatedRowHeight = 50
        tableView.tableFooterView = UIView(frame: CGRect.zero)
        tableView.register(UINib(nibName: cellId, bundle: nil), forCellReuseIdentifier: cellId)
        tableView.delegate = self
        tableView.dataSource = self
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! CellView
        let row: TableRowDM = self.rowData[indexPath.row]

        cell.title.text = row.title
        cell.subtitle.text = row.subTitle

        if row.hidden {
            // hide subtitle
            cell.subtitleHeight.isActive = true
            cell.titleToSubtitleGap.constant = 0
        } else {
            // show subtitle
            cell.subtitleHeight.isActive = false
            cell.titleToSubtitleGap.constant = 16
        }
        cell.setNeedsLayout()
        cell.layoutIfNeeded()
        return cell
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return rowData.count
    }
}

这似乎不起作用,subtitleLabel没有隐藏,行高始终相同。当subtitleLabel和titleLabel的内容更改时,高度将调整为那些UIViews中的文本,但是当我尝试使用约束进行操作时,它将无法正常工作。有什么帮助吗?我究竟做错了什么? CellView是带有此类子视图的非常简单的视图

-----------------
      |
- titleLabel ----
      |
      | <- titleToSubtitleGap
      |
- subtitleLabel -
      |
-----------------

1 个答案:

答案 0 :(得分:0)

我为我的问题找到了解决方案。它没有记录在任何地方,但可以使用。

重要的是。如果您支持横向/纵向,则重要的是在NSLayoutConstraints中设置正确的优先级,该优先级如here所述定义垂直的不间断的约束和视图链(具有定义的高度)-他们写道:

  

接下来,在单元格内容中布置表格视图单元格的内容   视图。要定义单元格的高度,您需要一个不间断的链   约束和视图(具有定义的高度)以填充之间的区域   内容视图的顶部边缘和底部边缘。如果您有意见   固有含量高度,系统将使用这些值。如果没有,你   必须在视图或视图中添加适当的高度限制   内容视图本身。

他们没有写的是,最终定义表格单元格contentView高度的所有垂直约束的优先级必须小于所需的优先级(1000)(如果需要支持多种设备方向)。我在示例中将那些优先级设置为“高(750)”。

接下来,重要的是在updateConstraints方法中使用这些约束进行更改。因此,如果您正在设计UIView,并且当该视图的某些属性需要更改约束时,则需要在视图中执行类似的操作,以便在适当时机调用updateConstraints方法。设置属性新值后,调用needsUpdateConstraints(),以便AutoLayout知道UIView中的约束已更改。

import UIKit

class ListItemTableViewCell: UITableViewCell {

    @IBOutlet weak var subtitleToTitleGap: NSLayoutConstraint!
    @IBOutlet weak var subtitleHeightZero: NSLayoutConstraint!

    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var subtitleLabel: UILabel!

    public var isSubtitleHidden: Bool {
        get {
            return self.subtitleLabel.isHidden
        }
        set {
            // Here we are changing property that change layout in our view
            self.subtitleLabel.isHidden = newValue
            self.needsUpdateConstraints() // Here we are telling AutoLayout to recalculate constraints in `updateConstraints` method.
        }
    }

#if !TARGET_INTERFACE_BUILDER
    // We need to block this code from running in Interface Builder
    override func updateConstraints() {
        if self.isSubtitleHidden {
            // Hide Subtitle
            self.subtitleToTitleGap.constant = 0.0
            self.subtitleHeightZero.autoInstall() // This makes subtitle height to be 0
        } else {
            // Show Subtitle
            self.subtitleToTitleGap.constant = 2.0
            self.subtitleHeightZero.autoRemove() // This will make subtitle height to be self calculated
        }
        super.updateConstraints() // don't forget to call this after You finished constraints changes
    }
#endif
}

最重要的也是我最难发现的是,在接口构建器中隐藏updateConstraints的实现,因为这导致IB在xib编辑器中不时崩溃。在上面查看此示例。该方法包装在#if预处理程序宏中。所以我认为就这样。祝你好运!