代码之家  ›  专栏  ›  技术社区  ›  mfaani

如何正确获取tableViewCell的contentView绑定大小?

  •  0
  • mfaani  · 技术社区  · 7 年前

    资源:

    我读过很多关于 Using Auto Layout in UITableView for dynamic cell layouts & variable row heights

    并听从了他们的建议,但没用。

    要复制的设置:

    如果复制/粘贴 MyTableViewCell ViewController 片段:然后你可以重现这个问题。

        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()
            }
        }
    

    以下 视图控制器 包含我的表视图:

    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])
        }
    }
    

    honey image 我用过。我把它的尺寸定为 44 * 44

    主要问题

    我的主要问题在里面 cellForRowAtIndex :

    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
    

    似乎不对。

    问题1:

    enter image description here

    问题2: 我在调用之前和之后记录contentView的绑定 cell.layoutSubviews 它从 320 260 但最终在viewDebugger中显示为 308 !!!

    我已经从问题中删除了一些其他截图。它们大多杂乱无章,但也许值得一看。您可以查看修订历史记录。

    1 回复  |  直到 7 年前
        1
  •  1
  •   DonMag    7 年前

    我相信这个问题与使用默认单元格的 imageView .

    .image 属性,因此在cell init上,您将自定义标签约束为0,0,0,0的图像视图

    然后,在 cellForRowAt ,您设置了 .图像 财产,还有它 出现 那个动作 设置contentView高度。我在上面找不到任何文档,在debug中挖掘也找不到任何冲突的约束,所以我不完全确定为什么会发生这种情况。

    两种选择:

    .numberOfLines 默认情况下 .textLabel 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的高度将超过单元格的高度:

    enter image description here

    至少 单元格的顶部和底部:

    enter image description here

    而且,使用一些填充可能会看起来更好一些,因此我们将imageView的顶部和底部约束为 至少

    enter image description here

    如果需要,我们还可以通过将标签的底部约束为 至少 确切地

    enter image description here

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