UITableViewAutomaticDimension 似乎没有考虑细节标签的内容。我该如何解决?

UITableViewAutomaticDimension doesn't seem to take the detail label's content into account. How can I fix it?

我已经阅读了很多有关如何使 table 单元格的高度根据单元格中的文本自动调整的内容。从 , this and this 开始,似乎 UITableView.automaticDimension 是要走的路。显然它也适用于系统(即非自定义)单元格。

在我的 table 视图中,我使用了一堆 "Right Detail" 样式的系统单元格(请注意,这不是我的自定义单元格):

我让两个标签的 "Lines" 都为 0,"Line Break" 为 "Word Wrap" 以启用自动换行。现在,在我的 table VC 中,我使用一些旨在显示我遇到的问题的数据填充了 table 视图:

var strings = [("Some Very Long Black Text That Doesn't Fit. Foo Bar Baz.", ""),
               ("A", "Short Text"),
               ("B", "Foo Bar"),
               ("Not working:", "Some Very Long Grey Text That Doesn't Fit. Foo Bar Baz"),
               ("C", "Another Text")]

override func numberOfSections(in tableView: UITableView) -> Int {
    return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return strings.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
    cell?.textLabel?.text = strings[indexPath.row].0
    cell?.detailTextLabel?.text = strings[indexPath.row].1
    return cell!
}

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

这是结果:

如您所见,第一行正确调整了它的高度以适合标签,但第四行的标签溢出了它的单元格。 table 视图似乎只尝试使行高适合 textLabel.text,根本不考虑 detailTextLabel 中的文本。

如何让 table 视图在确定单元格高度时考虑两个标签的文本?


额外尝试:

我想我可以创建一个与系统 "Right Detail" 单元格样式非常相似的自定义单元格,然后改用该单元格。无论我做什么,我都无法重新创建系统单元的约束。例如,这是我的最佳尝试。

很难让它完全按照您的意愿工作。

自动布局使多个 "passes" 尝试满足布局要求。但是,想想它要做什么:

  • 设置两个标签的文本
  • "leftLabel"需要换行吗?
  • "rightLabel"需要换行吗?
  • 如果"leftLabel"换行,"rightLabel"还需要换行吗?
  • 反之亦然?

而且,在该过程中的什么时候可以确定 两个 标签最终都会换行,所以每个标签都占宽度的 50%?请记住,单元格宽度会因设备和方向而异?

可能想要查看您希望显示的实际数据,并考虑它的实际外观。 "right detail cell" 设计可能不会成为最佳布局。

就是说 - 这是一个自定义单元格,可能 适合您。它将需要大量的边缘测试来查看是否有任何尺寸被丢弃(例如,通过 very 长字符串 and/or字符串长度之间的差异非常大):

class MyRightDetailCell: UITableViewCell {

    var myTextLabel: UILabel = {
        let v = UILabel()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.numberOfLines = 0
        v.textAlignment = .left
        v.font = UIFont.systemFont(ofSize: 17.0)
        v.textColor = .black
        v.setContentHuggingPriority(.required, for: .horizontal)
        v.setContentHuggingPriority(.required, for: .vertical)
        v.setContentCompressionResistancePriority(.required, for: .horizontal)
        v.setContentCompressionResistancePriority(.required, for: .vertical)
        return v
    }()

    var myDetailTextLabel: UILabel = {
        let v = UILabel()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.numberOfLines = 0
        v.textAlignment = .right
        v.font = UIFont.systemFont(ofSize: 17.0)
        v.textColor = .darkGray
        v.setContentHuggingPriority(.required, for: .horizontal)
        v.setContentHuggingPriority(.required, for: .vertical)
        v.setContentCompressionResistancePriority(.required, for: .horizontal)
        v.setContentCompressionResistancePriority(.required, for: .vertical)
        return v
    }()

    var theStackView: UIStackView = {
        let v = UIStackView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.axis = .horizontal
        v.alignment = .top
        v.distribution = .fill
        v.spacing = 8
        v.setContentHuggingPriority(.required, for: .vertical)
        v.setContentCompressionResistancePriority(.required, for: .vertical)
        return v
    }()

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

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }

    func commonInit() -> Void {

        contentView.addSubview(theStackView)
        theStackView.addArrangedSubview(myTextLabel)
        theStackView.addArrangedSubview(myDetailTextLabel)

        let g = contentView.layoutMarginsGuide

        NSLayoutConstraint.activate([
            theStackView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
            theStackView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
            theStackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
            theStackView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
        ])

    }

}

class RightDetailTableViewController: UITableViewController {

    var strings = [
        ("Some Very Long Black Text That Doesn't Fit. Foo Bar Baz.", ""),
        ("A", "Short Text"),
        ("B Somewhat Much Longer Left Label Text.", "With long right label text."),
        ("Working Now?", "Some Very Long Grey Text (Three Lines?) That Doesn't Fit. Foo Bar Baz"),
        ("C", "Another Text"),
        ("D", "With Long Right Label Text That Will Need To Wrap."),
    ]

    let cellID = "MYRDC"

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.register(MyRightDetailCell.self, forCellReuseIdentifier: cellID)

        // make 5 copies of the test data so we'll have plenty of rows
        // for scrolling (to check cell re-use)
        for _ in 1...5 {
            strings.append(contentsOf: strings)
        }
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        // due to the complexity of the cell, the layout is more reliable if the
        // table is reloaded here - try it with and without this extra reloadData() call
        tableView.reloadData()
    }

    // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return strings.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath) as! MyRightDetailCell

        cell.myTextLabel.text = strings[indexPath.row].0
        cell.myDetailTextLabel.text = strings[indexPath.row].1

        // if either label has no text (""), set it to hidden
        // to remove the stack view's spacing
        cell.myTextLabel.isHidden = (strings[indexPath.row].0 == "")
        cell.myDetailTextLabel.isHidden = (strings[indexPath.row].1 == "")

        // un-comment the next two lines to show label background colors
        // to make it easy to see the label frames
        //cell.myTextLabel.backgroundColor = .cyan
        //cell.myDetailTextLabel.backgroundColor = .green

        return cell
    }

}

输出: