【问题标题】:UITableViewCell constraints for dynamic height break动态高度中断的 UITableViewCell 约束
【发布时间】:2019-05-07 22:34:33
【问题描述】:

我有一个简单的 UITableViewCell 子类,其中有一个 titleLabel 属性(单元格有更多视图,但为了显示问题,我只会做一个标签,因为它也会损坏)。

这是我的标签代码:

    self.titleLabel = UILabel(frame: .zero)
    self.titleLabel.numberOfLines = 0
    self.titleLabel.font = UIFont.preferredFont(forTextStyle: .headline)
    self.titleLabel.textColor = UIColor.white
    self.titleLabel.adjustsFontSizeToFitWidth = false
    self.titleLabel.textAlignment = .left
    self.titleLabel.translatesAutoresizingMaskIntoConstraints = false
    self.contentView.addSubview(self.titleLabel)

    self.titleLabel.topAnchor.constraint(equalTo: self.artworkImageView.topAnchor).isActive = true
    self.titleLabel.leftAnchor.constraint(equalTo: self.artworkImageView.rightAnchor, constant: 10.0).isActive = true
    self.titleLabel.rightAnchor.constraint(equalTo: self.contentView.rightAnchor, constant: -10.0).isActive = true
    self.titleLabel.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor).isActive = true

我也这样设置我的UITableView

self.tableView.rowHeight = UITableView.automaticDimension
self.tableView.estimatedRowHeight = 50.0

但它不断打破约束并出现如下错误:

"<NSLayoutConstraint:0x28211ce10 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x10859a4f0.height == 4.33333   (active)>"

有更多的限制,但是这个说我的单元格内容视图只有 4.3 的高度,但是我希望它随着标签的增长而增长。

我也尝试设置 contentHuggingPriorities 和底部锚的优先级。我还将它与在线代码或我在网上看到的 IB 约束进行了比较,它们都设置了 4 个约束:上、左、下、右。 我还尝试了前导和尾随而不是左右 - 结果相同。

任何帮助表示赞赏

这是我完整的 AlbumTableViewCell:

class AlbumTableViewCell: UITableViewCell {

    public private(set) var artworkImageView: UIImageView
    public private(set) var titleLabel: UILabel
    public private(set) var albumInfoLabel: UILabel
    public private(set) var artistNameLabel: UILabel

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

        self.artworkImageView = UIImageView(frame: .zero)
        self.titleLabel = UILabel(frame: .zero)
        self.albumInfoLabel = UILabel(frame: .zero)
        self.artistNameLabel = UILabel(frame: .zero)

        super.init(style: style, reuseIdentifier: reuseIdentifier)

        self.tintColor = UIColor.white
        self.backgroundColor = UIColor.clear

        self.contentView.backgroundColor = UIColor.barTintColor
        self.contentView.layer.masksToBounds = false
        self.contentView.layer.cornerRadius = 10.0

        self.artworkImageView.layer.cornerRadius = 10.0
        self.artworkImageView.layer.masksToBounds = true
        self.artworkImageView.contentMode = .scaleAspectFit
        self.artworkImageView.translatesAutoresizingMaskIntoConstraints = false
        self.contentView.addSubview(self.artworkImageView)

        // image view
        self.artworkImageView.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 5).isActive = true
        self.artworkImageView.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 5).isActive = true
        self.artworkImageView.widthAnchor.constraint(equalToConstant: 80).isActive = true
        self.artworkImageView.heightAnchor.constraint(equalToConstant: 80).isActive = true

        self.titleLabel = UILabel(frame: .zero)
        self.titleLabel.numberOfLines = 2
        self.titleLabel.font = UIFont.preferredFont(forTextStyle: .headline)
        self.titleLabel.textColor = UIColor.white
        self.titleLabel.adjustsFontSizeToFitWidth = false
        self.titleLabel.textAlignment = .left
        self.titleLabel.translatesAutoresizingMaskIntoConstraints = false
        self.contentView.addSubview(self.titleLabel)

        // title
        self.titleLabel.leadingAnchor.constraint(equalTo: self.artworkImageView.trailingAnchor, constant: 5.0).isActive = true
        self.titleLabel.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 5.0).isActive = true
        self.titleLabel.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -5.0).isActive = true
        self.titleLabel.heightAnchor.constraint(equalToConstant: 35).isActive = true

        self.albumInfoLabel.numberOfLines = 1
        self.albumInfoLabel.font = UIFont.preferredFont(forTextStyle: .subheadline)
        self.albumInfoLabel.textColor = UIColor.lightGray
        self.albumInfoLabel.adjustsFontSizeToFitWidth = true
        self.albumInfoLabel.textAlignment = .left
        self.albumInfoLabel.translatesAutoresizingMaskIntoConstraints = false
        self.contentView.addSubview(self.albumInfoLabel)

        // albumInfoLabel
        self.albumInfoLabel.topAnchor.constraint(equalTo: self.titleLabel.bottomAnchor, constant: 5.0).isActive = true
        self.albumInfoLabel.leadingAnchor.constraint(equalTo: self.titleLabel.leadingAnchor).isActive = true
        self.albumInfoLabel.trailingAnchor.constraint(equalTo: self.titleLabel.trailingAnchor).isActive = true
        self.albumInfoLabel.heightAnchor.constraint(equalToConstant: 35).isActive = true

        self.artistNameLabel = UILabel(frame: .zero)
        self.artistNameLabel.numberOfLines = 1
        self.artistNameLabel.font = UIFont.preferredFont(forTextStyle: .subheadline)
        self.artistNameLabel.textColor = UIColor.lightGray
        self.artistNameLabel.adjustsFontSizeToFitWidth = true
        self.artistNameLabel.textAlignment = .left
        self.artistNameLabel.translatesAutoresizingMaskIntoConstraints = false
        self.contentView.addSubview(self.artistNameLabel)

        // albumInfoLabel
        self.artistNameLabel.topAnchor.constraint(equalTo: self.albumInfoLabel.bottomAnchor, constant: 5.0).isActive = true
        self.artistNameLabel.leadingAnchor.constraint(equalTo: self.albumInfoLabel.leadingAnchor).isActive = true
        self.artistNameLabel.trailingAnchor.constraint(equalTo: self.albumInfoLabel.trailingAnchor).isActive = true
        self.artistNameLabel.heightAnchor.constraint(equalToConstant: 35).isActive = true

        let selectedView: UIView = UIView(frame: .zero)
        selectedView.backgroundColor = UIColor.gray
        selectedView.layer.cornerRadius = 10.0
        selectedView.layer.masksToBounds = false
        self.selectedBackgroundView = selectedView
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        let contentViewFrame = self.contentView.frame
        let insetContentViewFrame = contentViewFrame.inset(by: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10))
        self.contentView.frame = insetContentViewFrame

        self.selectedBackgroundView?.frame = insetContentViewFrame
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

此代码不再崩溃,但单元格不会自动调整大小(见图)。。浅灰色区域为内容视图

此代码不再打破任何约束,但单元格也不会自动计算高度。这是我的表格视图控制器:

self.tableView.register(AlbumTableViewCell.self, forCellReuseIdentifier: "AlbumCell")
self.tableView.separatorStyle = .none
self.tableView.tableFooterView = UIView(frame: .zero)
self.tableView.rowHeight = UITableView.automaticDimension
self.tableView.estimatedRowHeight = 50.0

【问题讨论】:

  • "(单元格有更多的视图,但为了显示问题,我只做一个标签,因为它也坏了)" 破损可能是因为contentView 中的任何其他约束。所以,重要的是你分享所有这些或创建一个Minimal, Complete, and Verifiable Example
  • 不,我什至测试了上面的代码(我注释掉了其他视图),就这样。仅此标签就与上面的代码中断了
  • 如果你注释掉其他代码,你的前两个约束会给你一个错误。
  • @JanoschHübner 分享问题的演示
  • 如果不查看表格视图单元格的其他 UI 元素,任何人都不太可能提供帮助。如果你担心代码太多,那么你应该制作一个模拟你的问题的演示并将其上传到某个地方并在你的问题中附加链接。

标签: ios swift autolayout nslayoutconstraint


【解决方案1】:
        var titleLabel = UILabel()

        contentView.addSubview(titleLabel)
        titleLabel.textColor = UIColor(red:0.32, green:0.17, blue:0.12, alpha:1.0)
        titleLabel.font = UIFont.boldSystemFont(ofSize: 16.0)
        titleLabel.translatesAutoresizingMaskIntoConstraints = false
        titleLabel.topAnchor.constraint(equalTo: marginGuide.topAnchor).isActive = true
        titleLabel.trailingAnchor.constraint(equalTo: marginGuide.trailingAnchor, constant: 8).isActive = true
        titleLabel.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor).isActive = true
        titleLabel.leadingAnchor.constraint(equalTo: marginGuide.leadingAnchor, constant: 8).isActive = true

试试这个。

【讨论】:

    【解决方案2】:

    说实话,我不知道你的问题到底是什么,但我确定你在单元格约束方面遇到了麻烦,你想要一个动态单元格。

    假设您的单元格有 4 个视图 artWrokImageViewartNameLabelartDescriptionLabelartistNameLabel

    首先你需要确保这些视图从表格单元格的顶部和底部受到最大的约束,所以当你调用self.tableView.rowHeight = UITableView.automaticDimension时它知道如何动态扩展。

    其次,当视图出现时,您需要告诉表格展开

    这是上面 4 个视图的演示。

    表格视图控制器:

    class YourTableViewController : UITableViewController {
    
            let customTableCellID = "customTableCellID";
    
            override func viewDidLoad() {
                super.viewDidLoad();
                setupTable();
            }
    
            override func viewWillAppear(_ animated: Bool) {
                super.viewWillAppear(animated)
                self.tableView.estimatedRowHeight = 50;
                self.tableView.rowHeight = UITableView.automaticDimension;
            }
    
            fileprivate func setupTable() {
                tableView.register(YourCustomTableCell.self, forCellReuseIdentifier: customTableCellID);
            }
        }
        extension YourTableViewController {
    
            override func numberOfSections(in tableView: UITableView) -> Int {
                return 1;
            }
            override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
                return 1;
            }
    
            override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
                let cell = tableView.dequeueReusableCell(withIdentifier: customTableCellID, for: indexPath) as! YourCustomTableCell
                cell.artistNameLabel.text = "the real art";
                cell.artworkImageView.image = UIImage(named: "mazen");
                cell.artDescriptionLabel.text = "long long long long long long long long long long long long long long long long long long long long long long long long long description";
                cell.artNameLabel.text = "someting"
                return cell
            }
        }
    

    单元格:

    class YourCustomTableCell : UITableViewCell {
    
    
        var artworkImageView : UIImageView = {
            let imageView = UIImageView();
            imageView.translatesAutoresizingMaskIntoConstraints = false;
            return imageView;
        }()
        var artNameLabel : UILabel = {
            let label = UILabel();
            label.font = UIFont.boldSystemFont(ofSize: 20);
            label.translatesAutoresizingMaskIntoConstraints = false;
            return label
        }()
        var artDescriptionLabel : UILabel = {
            let label = UILabel();
            label.textColor = .darkGray;
            label.numberOfLines = 0;
            label.translatesAutoresizingMaskIntoConstraints = false;
            return label;
        }()
        var artistNameLabel : UILabel = {
            let label = UILabel();
            label.textColor = .blue;
            label.translatesAutoresizingMaskIntoConstraints = false;
            return label;
        }()
    
    
        override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
            super.init(style: style, reuseIdentifier: reuseIdentifier);
            setupCell();
        }
        fileprivate func setupCell() {
    
            // add views
            contentView.addSubview(artworkImageView);
            contentView.addSubview(artNameLabel);
            contentView.addSubview(artDescriptionLabel);
            contentView.addSubview(artistNameLabel);
    
            // layout views
    
            // image view
            artworkImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 5).isActive = true;
            artworkImageView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 5).isActive = true;
            artworkImageView.widthAnchor.constraint(equalToConstant: 80).isActive = true;
            artworkImageView.heightAnchor.constraint(equalToConstant: 80).isActive = true;
    
            // art name
            artNameLabel.leadingAnchor.constraint(equalTo: artworkImageView.trailingAnchor, constant: 5).isActive = true;
            artNameLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 5).isActive = true;
            artNameLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -5).isActive = true;
            artNameLabel.heightAnchor.constraint(equalToConstant: 35).isActive = true;
    
            // descripion
            artDescriptionLabel.leadingAnchor.constraint(equalTo: artworkImageView.trailingAnchor, constant: 5).isActive = true;
            artDescriptionLabel.topAnchor.constraint(equalTo: artNameLabel.bottomAnchor, constant: 5).isActive = true;
            artDescriptionLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -5).isActive = true;
            // in art description label don't set the height anchors so it can expand
    
            // artist name
            artistNameLabel.leadingAnchor.constraint(equalTo: artworkImageView.trailingAnchor, constant: 5).isActive = true;
            artistNameLabel.topAnchor.constraint(equalTo: artDescriptionLabel.bottomAnchor, constant: 5).isActive = true;
            artistNameLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -5).isActive = true;
            artistNameLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -5).isActive = true; // this constraint is requierd for dynamic cell
    
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError()
        }
    }
    

    如果这个答案不适合你,请告诉我。

    【讨论】:

    • 将尽快对此进行测试!谢谢你的回答:)
    • 一个问题:您将视图添加到单元格本身而不是其内容视图。有什么理由吗?
    • @janoschHübner 我从来没有在 contentView 中添加过子视图,但我尝试过,但它不起作用,我认为您应该添加它以自行查看
    • @janoschHübner 好的,很抱歉,我将视图约束添加到 contentView 并且可以正常工作。
    • 好的,有什么变化吗?否则我会在内容视图上尝试您的代码
    【解决方案3】:

    您的 tableviewcells 知道它们的高度应该是多少。这很好。

    你只需要给出足够的约束,它就可以解决它。你不这样做!

    我目前看到的主要问题是artworkImageView 不限于top bottom。每个视图的垂直公理都需要从上到下进行约束。

    你的第一个垂直公理就是图像。它没有底部约束。添加那个。所以 tableviewcell 知道它需要调整自己的大小。我强烈建议您查看this moment of WWDC

    同样在this moment 的同一视频前面,它强烈建议您将视图转储到多个堆栈视图中并以这种方式进行组织。所以这也是一种选择。

    PS:

    1. 不要转储self。它只是增加了线宽而没有额外的好处。

    2. 将所有与布局无关的标签/图像设置移动到它们自己的实例中。例如

    lazy label : UILabel = {
       let label = UILabel()
       label.text = "John"
       label.translatesAutoresizingMaskIntoConstraints = false
       return label
    }()
    
    1. 不用提frame : .zero。只是UILabel() 意味着帧为零。
    2. 最好从小的 UI 开始,然后不断向其中添加更多元素。这样调试你的布局变得更容易/更小。

    【讨论】:

    • 如果我为图像设置了顶部和底部,它会将 200x200 的整个图像高度放入单元格中,如果我随后为图像视图设置高度约束,它会尝试打破该约束
    • 我建议您观看整个视频。但是,如果您没有时间,请参阅here。就像在使用屏幕的宽度并基于它得出一个宽度一样。基于宽度得出一个高度。还有它为什么要打破它?还有什么其他限制。
    • 我正在尝试,但它总是在我身上中断,或者单元格没有调整大小。这真的很奇怪。 :\
    • 我建议您将代码减半。做点小事。删除不相关的布局代码,然后重试。像这样很难。也尝试使用视图调试层次结构。这非常有帮助。您可以查看哪些约束处于活动状态或被忽略...
    • 我现在只从标题标签开始,我让它成长,让我们从这里看看
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-05-31
    • 1970-01-01
    • 1970-01-01
    • 2021-05-25
    • 1970-01-01
    • 2018-06-06
    • 1970-01-01
    相关资源
    最近更新 更多