为啥 UIStackView 中的 UILabel 在重用时会失去高度?

Posted

技术标签:

【中文标题】为啥 UIStackView 中的 UILabel 在重用时会失去高度?【英文标题】:Why do UILabel inside UIStackView lose its height when reusing occurs?为什么 UIStackView 中的 UILabel 在重用时会失去高度? 【发布时间】:2020-01-08 15:00:35 【问题描述】:

当单元格被重用时,UIStackView 中的 UILabel 开始失去它们的大小。可能与什么有关。对于表格,单元格高度在 UITableView.automaticDimension 中设置。

我不明白这与重用有什么关系,因为我使用的是 prepareForReuse。

class SalesPointTableViewCell: UITableViewCell 

private var nameLabel = UILabel()

private var someView: UIStackView = 
    $0.distribution = .fill
    $0.translatesAutoresizingMaskIntoConstraints = false
    $0.setContentCompressionResistancePriority(UILayoutPriority.required, for: .vertical)

    return $0
(UIStackView())

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) 
    super.init(style: style, reuseIdentifier: reuseIdentifier)
    translatesAutoresizingMaskIntoConstraints = false
    contentView.translatesAutoresizingMaskIntoConstraints = false
    contentView.addSubview(nameLabel)
    contentView.addSubview(someView)
    nameLabel.snp.makeConstraints 
        $0.left.top.equalToSuperview()
    
    someView.snp.makeConstraints 
        $0.left.right.width.equalToSuperview()
        $0.top.equalTo(nameLabel.snp.bottom)
    


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

override func prepareForReuse() 
    super.prepareForReuse()
    nameLabel.text = nil
    someView.arrangedSubviews.forEach $0.removeFromSuperview()


func configure(name: String, points: [String]) 
    self.nameLabel.text = name
    points.forEach  pointName in
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.numberOfLines = 1
        label.text = pointName
        self.someView.addArrangedSubview(label)
        label.sizeToFit()
    


【问题讨论】:

【参考方案1】:

几件事...

不要在cell 或其.contentView 上设置.translatesAutoresizingMaskIntoConstraints 您正在为堆栈视图设置leadingtrailing 约束,因此无需设置其width 您缺少bottom 约束

试试这个:

class SalesPointTableViewCell: UITableViewCell 

    private var nameLabel = UILabel()

    private var someView: UIStackView = 

        $0.distribution = .fill

        // add a little spacing between arranged subviews so we can see them
        $0.spacing = 4

        $0.translatesAutoresizingMaskIntoConstraints = false
        $0.setContentCompressionResistancePriority(UILayoutPriority.required, for: .vertical)

        return $0
    (UIStackView())

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

        // no no no
        //translatesAutoresizingMaskIntoConstraints = false

        // no no no
        //contentView.translatesAutoresizingMaskIntoConstraints = false

        contentView.addSubview(nameLabel)
        contentView.addSubview(someView)

        nameLabel.snp.makeConstraints 
            $0.left.top.equalToSuperview()
        

        // you're missing a bottom constraint
        //someView.snp.makeConstraints 
        //  $0.left.right.width.equalToSuperview()
        //  $0.top.equalTo(nameLabel.snp.bottom)
        //

        someView.snp.makeConstraints 
            // costraining left and right, so no need for width
            //  $0.left.right.width.equalToSuperview()
            $0.left.right.equalToSuperview()

            $0.top.equalTo(nameLabel.snp.bottom)

            // need to add a bottom constraint
            $0.bottom.equalToSuperview()
        

        // give nameLabel a cyan background to make it easy to see the frame
        nameLabel.backgroundColor = .cyan
    

    required init?(coder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    
    override func prepareForReuse() 
        super.prepareForReuse()
        nameLabel.text = nil
        someView.arrangedSubviews.forEach $0.removeFromSuperview()
    

    func configure(name: String, points: [String]) 
        self.nameLabel.text = name
        points.forEach  pointName in
            let label = UILabel()
            label.translatesAutoresizingMaskIntoConstraints = false

            // give labels a green background to make it easy to see the frames
            label.backgroundColor = .green
            label.textAlignment = .center

            label.numberOfLines = 1
            label.text = pointName
            self.someView.addArrangedSubview(label)

            // sizeToFit is not needed
            //label.sizeToFit()
        
    


class SalesTableViewController: UITableViewController 

    var theData: [[String]] = [[String]]()

    override func viewDidLoad() 
        super.viewDidLoad()

        tableView.register(SalesPointTableViewCell.self, forCellReuseIdentifier: "SPCell")

        // generate 50 "sales points"
        // each "sales point" will have 1, 2 or 3 labels
        for i in 1...50 
            let n = Int.random(in: 1...3)
            var a: [String] = [String]()
            for j in 1...n 
                a.append("Point: \(i) / \(j)")
            
            theData.append(a)
        

    

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

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return theData.count
    

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 

        let cell = tableView.dequeueReusableCell(withIdentifier: "SPCell", for: indexPath) as! SalesPointTableViewCell

        let a = theData[indexPath.row]

        cell.configure(name: "Test \(indexPath.row)", points: a)

        return cell

    


结果:

并且,在滚动之后,您可以看到它们被重复使用:

【讨论】:

以上是关于为啥 UIStackView 中的 UILabel 在重用时会失去高度?的主要内容,如果未能解决你的问题,请参考以下文章

UIStackView 中的 UILabel

UIStackView 中的 UILabel 中的 CAGradientLayer

将 UIStackView 中的 UILabel 背景更改为渐变色

UITableViewCell 中的 UIStackView 与多行 UILabel

UIStackView 中的多行 UILabel 问题

iOS:多行 UILabel 与水平 UIStackView 中的另一个 UILabel 大小错误