首页 > 解决方案 > UITableViewAutomaticDimension changes multiple times

问题描述

I have a project that uses iOS's default dynamic font. As so, I'm using UITableViewAutomaticDimension for the height. I'm doing everything programmatically, so I set the constraints for my custom cells.

My Understanding of Constraints and UITableViewCells

From what I can understand about constraints, is that when you apply them to the superView, the attributes that you set to be constrained will be forced to move based on the size of the superView. But if you set the constraints to the contentView of the UITableViewCell, then the attribute that you set the constraints to will force the UITableViewCell to change its height, width, ext.

CustomTableViewCell

import SnapKit

lazy var loadingIndicator: UIActivityIndicatorView = {
    let loadingIndicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
    loadingIndicator.sizeToFit()

    loadingIndicator.autoresizingMask = [UIViewAutoresizing.flexibleHeight]
    loadingIndicator.hidesWhenStopped = true

    return loadingIndicator
}()

lazy var logoImageView: UIImageView = {
    let logoImageView = UIImageView()

    logoImageView.contentMode = UIViewContentMode.scaleAspectFit
    logoImageView.backgroundColor = UIColor.white

    logoImageView.clipsToBounds = true

    return logoImageView
}()

lazy var storeLabel: UILabel = {
    let storeLabel = UILabel()
    storeLabel.sizeToFit()

    storeLabel.font = UIFont.preferredFont(forTextStyle: .footnote)
    storeLabel.adjustsFontForContentSizeCategory = true
    storeLabel.textColor = UIColor.lightGray

    return storeLabel
}()

lazy var priceLabel: UILabel = {
    let priceLabel = UILabel()
    priceLabel.sizeToFit()

    priceLabel.font = UIFont.preferredFont(forTextStyle: .title1)
    priceLabel.adjustsFontForContentSizeCategory = true
    priceLabel.textColor = UIColor.black

    return priceLabel
}()

lazy private var stackView: UIStackView = {
    let stackView = UIStackView()
    stackView.sizeToFit()

    stackView.axis = UILayoutConstraintAxis.vertical
    stackView.alignment = UIStackViewAlignment.leading
    stackView.distribution = UIStackViewDistribution.fill
    stackView.spacing = 0

    return stackView
}()

lazy private var priceStackView: UIStackView = {
    let priceStackView = UIStackView()
    priceStackView.sizeToFit()

    priceStackView.axis = UILayoutConstraintAxis.horizontal
    priceStackView.alignment = UIStackViewAlignment.center
    priceStackView.distribution = UIStackViewDistribution.fill
    priceStackView.spacing = 0

    return priceStackView
}()

override func draw(_ rect: CGRect) {
    super.draw(rect)

    let margins = contentView.layoutMarginsGuide

    contentView.addSubview(logoImageView)
    contentView.addSubview(stackView)

    stackView.addArrangedSubview(storeLabel)
    stackView.addArrangedSubview(priceStackView)

    priceStackView.addArrangedSubview(loadingIndicator)
    priceStackView.addArrangedSubview(priceLabel)

    logoImageView.snp.makeConstraints { (make) in
        make.left.equalTo(layoutMarginsGuide).offset(layoutMargins.left * 0.5)
        make.centerY.height.equalTo(layoutMarginsGuide)
        make.width.equalTo(logoImageView.snp.height)
    }

    stackView.snp.makeConstraints { (make) in
        make.left.equalTo(logoImageView.snp.right).offset(layoutMargins.left * 1.5)
        make.right.centerY.equalTo(margins)
        make.height.equalTo(margins).offset(-15)
    }
}

Output

Everything should work perfectly fine, but I don't get the desired height.

First height, a second later the height changes to this, and the final desired height is this. Am I setting the constraints wrong, or is this just a general iOS bug?

标签: swiftuitableviewconstraintsprogrammatically

解决方案


The issue turned out to be the function that I was declaring the constraints inside of. Instead of using the draw(_ rect: CGRect) override function, I needed to use the init(style: UITableViewCellStyle, reuseIdentifier: String?) override function. The only reason I could think of for this is because the draw function happens after the init and after the margins have been set. So the constraints don't have time to update.

Here is the updated draw(_ rect: CGRect) to init(style: UITableViewCellStyle, reuseIdentifier: String?) function:

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

    let margins = contentView.layoutMarginsGuide

    contentView.addSubview(logoImageView)
    contentView.addSubview(stackView)

    stackView.addArrangedSubview(storeLabel)
    stackView.addArrangedSubview(priceStackView)

    priceStackView.addArrangedSubview(loadingIndicator)
    priceStackView.addArrangedSubview(priceLabel)

    logoImageView.snp.makeConstraints { (make) in
        make.left.equalTo(layoutMarginsGuide).offset(layoutMargins.left * 0.5)
        make.centerY.height.equalTo(layoutMarginsGuide)
        make.width.equalTo(logoImageView.snp.height)
    }

    stackView.snp.makeConstraints { (make) in
        make.left.equalTo(logoImageView.snp.right).offset(layoutMargins.left * 1.5)
        make.right.centerY.equalTo(margins)
        make.height.equalTo(margins).offset(-15)
    }
}

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

推荐阅读