Почему UILabel внутри UIStackView теряет свою высоту при повторном использовании? - PullRequest
1 голос
/ 08 января 2020

При повторном использовании ячеек UILabels внутри UIStackView начинают терять свой размер. С чем это может быть связано. Для таблицы высоты ячеек задаются в 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 Ответ

0 голосов
/ 09 января 2020

Пара вещей ...

  • НЕ устанавливайте .translatesAutoresizingMaskIntoConstraints на cell или .contentView
  • Вы устанавливаете ограничения leading и trailing для стекового представления, поэтому нет необходимости устанавливать его 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

    }

}

Результат:

enter image description here

и, после прокрутки, чтобы вы могли видеть, что они используются повторно:

enter image description here

...