Ios 如何正确获取tableViewCell';内容视图的绑定大小是多少? 资源:

Ios 如何正确获取tableViewCell';内容视图的绑定大小是多少? 资源:,ios,swift,uitableview,autolayout,contentsize,Ios,Swift,Uitableview,Autolayout,Contentsize,我已经读了很多关于这个问题的答案 并遵循了他们的建议,但不起作用 要复制的设置: 如果复制/粘贴MyTableViewCell和ViewController片段,则可以重现该问题 我对MyTableViewCell进行了子类化,并添加了自己的标签 import UIKit class MyTableViewCell: UITableViewCell { lazy var customLabel : UILabel = { let lbl

我已经读了很多关于这个问题的答案

并遵循了他们的建议,但不起作用

要复制的设置: 如果复制/粘贴
MyTableViewCell
ViewController
片段,则可以重现该问题

我对MyTableViewCell进行了子类化,并添加了自己的标签

    import UIKit

    class MyTableViewCell: UITableViewCell {

        lazy var customLabel : UILabel = {
            let lbl = UILabel()
            lbl.translatesAutoresizingMaskIntoConstraints = false
            lbl.numberOfLines = 0
            return lbl
        }()

        override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
            super.init(style: style, reuseIdentifier: reuseIdentifier)
            setupLayout()
        }
        private func setupLayout(){
            contentView.addSubview(customLabel)

            let top = customLabel.topAnchor.constraint(equalTo: contentView.topAnchor)
            let bottom = customLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
            let leadingFromImage = customLabel.leadingAnchor.constraint(equalTo: imageView!.trailingAnchor, constant: 5)
            let trailing = customLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor)

            NSLayoutConstraint.activate([top, bottom, leadingFromImage, trailing])
        }

        required init?(coder aDecoder: NSCoder) {
            fatalError()
        }
    }
以下
ViewController
包含我的tableview:

import UIKit

class ViewController: UIViewController {

    var datasource = ["It would have been a great day had Manchester United Lost its \n game. Anyhow I hope tomorrow Arsenal will win the game"]

    lazy var tableView : UITableView = {
        let table = UITableView()
        table.delegate = self
        table.dataSource = self
        table.translatesAutoresizingMaskIntoConstraints = false
        table.estimatedRowHeight = 100
        table.rowHeight = UITableViewAutomaticDimension
        return table
    }()
    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(tableView)
        tableView.pinToAllEdges(of: view)
        tableView.register(MyTableViewCell.self, forCellReuseIdentifier: "id")
    }
}

extension ViewController: UITableViewDelegate, UITableViewDataSource {

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

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

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

        cell.customLabel.text = datasource[indexPath.row]
        logInfo(of: cell)

        cell.accessoryType = .detailDisclosureButton
        cell.imageView?.image = UIImage(named: "honey")
        cell.layoutSubviews()
        cell.customLabel.preferredMaxLayoutWidth = tableView.bounds.width
        logInfo(of: cell)
        print("---------")

        return cell
    }

    private func logInfo(of cell: MyTableViewCell){
        print("boundsWidth: \(cell.contentView.bounds.width) | maxLayoutWidth: \(cell.contentView.bounds.width - 44 - 15 - 5) | systemLayoutSizeFitting : \(cell.customLabel.systemLayoutSizeFitting(UILayoutFittingCompressedSize))")
    }    
}

extension UIView{

    func pinToAllEdges(of view: UIView){
        let leading = leadingAnchor.constraint(equalTo: view.leadingAnchor)
        let top = topAnchor.constraint(equalTo: view.topAnchor)
        let trailing = trailingAnchor.constraint(equalTo: view.trailingAnchor)
        let bottom = bottomAnchor.constraint(equalTo: view.bottomAnchor)        

        NSLayoutConstraint.activate([leading, top, trailing, bottom])
    }
}
我使用的链接。我已将其大小设置为
44*44

主要问题 我的主要问题是在
cellforrowatinex
中:

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

    cell.customLabel.text = datasource[indexPath.row]
    logInfo(of: cell)

    cell.accessoryType = .detailDisclosureButton
    cell.imageView?.image = UIImage(named: "honey")
    cell.layoutSubviews()
    cell.customLabel.preferredMaxLayoutWidth = cell.contentView.bounds.width
    logInfo(of: cell)
    print("---------")

    return cell
}
问题:

无论出于何种原因,分配给以下对象的值:

cell.customLabel.preferredMaxLayoutWidth
似乎不对

Q1:为什么

Q2:我在调用
单元格前后记录contentView的绑定。LayoutSubview
它从
320
切换到
260
,但最终在viewDebugger中显示为
308

为什么contenView的边界再次发生变化


我已经从问题中删除了一些其他截图。它们大部分都很杂乱,但可能值得一看。您可以查看修订历史记录。

我认为问题与使用默认单元格的
图像视图有关

图像视图本身在设置其
.image
属性之前不存在,因此在cell init上,您将自定义标签约束为0,0,0,0的图像视图

然后,在
cellForRowAt
中设置
.image
属性,该操作似乎还设置了contentView高度。我在上面找不到任何文档,在调试中我也找不到任何冲突的约束,所以我不能完全确定为什么会发生这种情况

两种选择:

1-不要创建和添加自定义标签,而是将默认的
.textLabel
上的
.numberOfLines
设置为
0
。这应该足够了

2-如果需要自定义标签,还可以添加自定义图像视图

备选方案2如下:

class MyTableViewCell: UITableViewCell {

    lazy var customLabel : UILabel = {
        let lbl = UILabel()
        lbl.translatesAutoresizingMaskIntoConstraints = false
        lbl.numberOfLines = 0
        lbl.setContentCompressionResistancePriority(.required, for: .vertical)
        return lbl
    }()

    lazy var customImageView: UIImageView = {
        let v = UIImageView()
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()

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

    private func setupLayout(){
        contentView.addSubview(customLabel)

        contentView.addSubview(customImageView)

        // constrain leading of imageView to be 15-pts from the leading of the contentView
        let imgViewLeading = customImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 15)

        // constrain width of imageView to 42-pts
        let imgViewWidth = customImageView.widthAnchor.constraint(equalToConstant: 42)

        // constrain height of imageView to be equal to width of imageView
        let imgViewHeight = customImageView.heightAnchor.constraint(equalTo: customImageView.widthAnchor, multiplier: 1.0)

        // center imageView vertically
        let imgViewCenterY = customImageView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor, constant: 0.0)

        // top and bottom constraints for the imageView also need to be set,
        // otherwise the image will exceed the height of the cell when there
        // is not enough text to wrap and expand the height of the label

        // constrain top of imageView to be *at least* 4-pts from the top of the cell
        let imgViewTop = customImageView.topAnchor.constraint(greaterThanOrEqualTo: contentView.topAnchor, constant: 4)

        // constrain bottom of imageView to be *at least* 4-pts from the bottom of the cell
        let imgViewBottom = customImageView.topAnchor.constraint(lessThanOrEqualTo: contentView.bottomAnchor, constant: -4)

        // constrain top of the label to be *at least* 4-pts from the top of the cell
        let top = customLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 4)

        // if you want the text in the label vertically centered in the cell
        // constrain bottom of the label to be *exactly* 4-pts from the bottom of the cell
        let bottom = customLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -4)

        // if you want the text in the label top-aligned in the cell
        // constrain bottom of the label to be *at least* 4-pts from the bottom of the cell
        // let bottom = customLabel.bottomAnchor.constraint(lessThanOrEqualTo: contentView.bottomAnchor, constant: -4)

        // constrain leading of the label to be 5-pts from the trailing of the image
        let leadingFromImage = customLabel.leadingAnchor.constraint(equalTo: customImageView.trailingAnchor, constant: 5)

        // constrain the trailing of the label to the trailing of the contentView
        let trailing = customLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor)

        NSLayoutConstraint.activate([
            top, bottom, leadingFromImage, trailing,
            imgViewLeading, imgViewCenterY, imgViewWidth, imgViewHeight,
            imgViewTop, imgViewBottom
            ])

    }

    required init?(coder aDecoder: NSCoder) {
        fatalError()
    }
}

class HoneyViewController: UIViewController {

    var datasource = [
        "It would have been a great day had Manchester United Lost its game. Anyhow I hope tomorrow Arsenal will win the game",
        "One line.",
        "Two\nLines.",
    ]

    lazy var tableView : UITableView = {
        let table = UITableView()
        table.delegate = self
        table.dataSource = self
        table.translatesAutoresizingMaskIntoConstraints = false
        table.estimatedRowHeight = 100
        table.rowHeight = UITableViewAutomaticDimension
        return table
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(tableView)
        tableView.pinToAllEdges(of: view)
        tableView.register(MyTableViewCell.self, forCellReuseIdentifier: "id")
    }
}

extension HoneyViewController: UITableViewDelegate, UITableViewDataSource {

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

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

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

        cell.customLabel.text = datasource[indexPath.row]
        logInfo(of: cell)

        cell.accessoryType = .detailDisclosureButton
        cell.customImageView.image = UIImage(named: "Honey")
        logInfo(of: cell)
        print("---------")

        return cell
    }

    private func logInfo(of cell: MyTableViewCell){
        print("boundsWidth: \(cell.contentView.bounds.width) | maxLayoutWidth: \(cell.contentView.bounds.width - 44 - 15 - 5) | systemLayoutSizeFitting : \(cell.customLabel.systemLayoutSizeFitting(UILayoutFittingCompressedSize))")
    }
}

extension UIView{

    func pinToAllEdges(of view: UIView){
        let leading = leadingAnchor.constraint(equalTo: view.leadingAnchor)
        let top = topAnchor.constraint(equalTo: view.topAnchor)
        let trailing = trailingAnchor.constraint(equalTo: view.trailingAnchor)
        let bottom = bottomAnchor.constraint(equalTo: view.bottomAnchor)

        NSLayoutConstraint.activate([leading, top, trailing, bottom])
    }
}
编辑:

还需要一些约束条件。如果单元格只有足够一行的文本(无换行),imageView高度将超过单元格的高度:

因此,我们向imageView添加顶部和底部约束,以至少适合单元格的顶部和底部:

加上一些填充,它可能会看起来更好一些,因此我们将imageView的顶部和底部限制为距单元格顶部和底部至少4-pts:

如果需要,我们还可以将标签中的文本“顶部对齐”,方法是将其底部约束为距底部至少4-pts,而不是距底部正好4-pts:


我编辑的代码中的注释应该解释这些差异。

可能会覆盖
MyTableViewCell
中的布局方法,比如
layoutSubviews
,并在那里设置
preferredMaxLayoutWidth
而不是
cellForRowAt:
,那么我的当前代码序列不应该具有相同的内容吗效果?忽略边界更改,您试图实现什么?很可能你不需要这个值来开始…@DonMag这是问题的初始版本。这就是如果我没有在
customLabel
上设置任何宽度约束时会发生的情况,它的侧面只有4个约束。使用
preferredMaxLayoutWidth
我试图为它设置一个宽度。因此,标签的高度将驱动该单元所需的最大高度。我将
cell.contentView.bounds.width
分配给标签的
首选MaxLayoutWidth
。但这并不准确。因此,在调用
layoutSubviews
之后,我分配了
preferredMaxLayoutWidth
。UI看起来更好,但还不够完美。与其创建和添加自定义标签,不如将默认.textLabel上的.numberOfLines设置为0。这应该足够了。同意。但我不想那样做。我想解决这个问题。追根究底,看看它为什么不起作用:)。让我试试你的解决方案……我记得当我编写这个时,我在想为什么
imageView!。trailingAnchor
没有失败。我的意思是,如果它是可选的,在实例化时不是
nil
,虽然是空的,但它很奇怪:/我真的不明白你为什么要从代码中编写布局。。。但我知道。在故事板或xib中创建自定义单元格(如果创建xib,则需要在tableview上注册)@Honey是正确的创建自定义图像视图和自定义标签正确设置约束。将行数设置为0,换行符设置为0。只要正确设置了约束条件,单元格就会展开。(别忘了将单元格行高设置为“自动”并估计为您所需的数字)根据代码编写布局时,您很可能会在那里出错,并将在接下来的一个小时内找出错误所在。@JaubSanestrzik-使用代码而不是(或与)故事板/界面生成器一起使用的原因很多很多。本例中的问题似乎与不正确的约束无关。