【问题标题】:UITableViewAutomaticDimension doesn't seem to take the detail label's content into account. How can I fix it?UITableViewAutomaticDimension 似乎没有考虑到详细标签的内容。我该如何解决?
【发布时间】:2020-01-18 13:39:45
【问题描述】:

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

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

我让两个标签的“行”为 0,“换行”为“换行”以启用换行。现在在我的表 VC 中,我用一些数据填充了表视图,这些数据旨在显示我遇到的问题:

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
}

这是结果:

如您所见,第一行正确调整其高度以适合标签,但第四行的标签溢出其单元格。似乎表格视图仅尝试将行高与textLabel.text 匹配,根本没有考虑detailTextLabel 中的文本。

如何让表格视图在计算单元格高度时同时考虑标签的文本?


其他尝试:

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

【问题讨论】:

  • 你有几个问题。首先,不要试图修改内置单元类型的属性——几乎总是会导致其他问题。其次,您必须决定如何处理一个单元格的左侧和右侧都有长文本的情况。您希望左标签还是右标签优先?您想将标签限制为单元格宽度的 2/3 吗?根据您的回答,您可能需要编写一些额外的代码逻辑来确定哪一侧得到什么宽度。
  • 哦,好的。我不知道我不应该修改内置单元类型的属性。我的意思是我只是在更改标签的样式......至于当两个标签都有长文本时我想要什么,我希望每个标签占据单元格宽度的一半。 @DonMag

标签: swift uitableview autolayout


【解决方案1】:

要让它完全按照你的意愿工作是很困难的。

自动布局会进行多次“通过”以尝试满足布局要求。但是,想想它必须做什么:

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

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

可能想要查看您希望显示的实际数据,并考虑它的实际外观。 “正确的细节单元”设计最终可能不是最好的布局。

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

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
    }

}

输出:

【讨论】:

  • “您可能希望查看您希望显示的实际数据,并考虑它的实际外观。”关于这一点,我将在左侧显示数据库中表的列名,在右侧显示记录的值。所以右边的包装可能比左边的要多得多。
  • 我希望 Autolayout 做的事情(但不能在代码中表达)是首先将左侧标签拉伸到单元格宽度的一半。设置它的文本。如果文本不换行,则调整标签大小以适合文本。之后,使用右侧标签的宽度。但可能“自动”布局不允许太多“手动”操作说明......
猜你喜欢
  • 1970-01-01
  • 2023-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-08
  • 1970-01-01
  • 2013-08-31
  • 1970-01-01
相关资源
最近更新 更多