动态高度中断的UITableViewCell约束

时间:2018-12-06 09:05:53

标签: ios swift autolayout nslayoutconstraint

我有一个简单的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")
    }
}

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

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

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

3 个答案:

答案 0 :(得分:0)

        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

尝试一下。

答案 1 :(得分:0)

说实话,我不知道您到底出了什么问题,但是我可以肯定地说,您在单元格约束方面表现不佳,并且您想要一个动态单元格。

因此,假设您有一个具有4个视图的单元格artWrokImageViewartNameLabelartDescriptionLabelartistNameLabel

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

第二,您需要告诉表格何时出现视图

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

Table View Controller:

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

如果您的回答不正确,请告诉我。

答案 2 :(得分:0)

您的tableviewcells 不知道它们的高度应该是多少。没关系

您只需要给出足够的约束,以便它可以解决问题。你不是那样做!

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

您的第一个垂直公理就是图像。它没有底部约束。加上。因此tableviewcell知道需要多少调整自身大小。我强烈建议您查看this moment of WWDC

enter image description here

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

PS:

  1. 请勿转储self。它只是增加了线宽而没有其他好处。

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

lazy label : UILabel = {
   let label = UILabel()
   label.text = "John"
   label.translatesAutoresizingMaskIntoConstraints = false
   return label
}()
  1. 无需提及frame : .zero。仅UILabel()表示帧为零。
  2. 始终最好先将UI从小开始,然后继续向其中添加更多元素。这样,调试布局就变得容易/小巧。