@Gravio

Как решить проблему с авто размером collection view?

Без вызова reloadData() выводит все правильно, вот так:
5cab342fa6f3c904156041.png

Но после вызова reloadData()
5cab346c21811072930954.png
или так:
5cab3475c212a425216595.png

Проблема возникает именно после вызова reloadData()

Код collectionView cell:
class ReadArticleCollectionViewCell: UICollectionViewCell
{
    @IBOutlet weak var pronunciationLabel: UILabel!
    @IBOutlet weak var wordLabel: UILabel!
    
    override func awakeFromNib() {
        super.awakeFromNib()
        
        contentView.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            contentView.leftAnchor.constraint(equalTo: leftAnchor),
            contentView.rightAnchor.constraint(equalTo: rightAnchor),
            contentView.topAnchor.constraint(equalTo: topAnchor),
            contentView.bottomAnchor.constraint(equalTo: bottomAnchor)
        ])
    }
}

5cab34fc605e1293565315.png

Вообщем нужна помощь, почему после reloadData() ломается авто размер
  • Вопрос задан
  • 706 просмотров
Решения вопроса 1
doublench21
@doublench21 Куратор тега Swift
Потому-что, так никто не делает, ну кроме Вас. Для работы auto layout нужно построить цепочку ограничений сверху вниз.

Что бы не спрашивать что у Вас есть, а что нет, сразу напишу полный список./

1) Прописать у layout коллекции:
// ...
layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
layout.itemSize = UICollectionViewFlowLayout.automaticSize


2) Соврал, нужно уточнение.

pronunciationLabel - Сколько строк должен занимать? (Только одну?) Увеличивается только в ширину?
wordLabel - Тоже, что и pronunciationLabel? Или увеличивается в высоту?

3) Этот бред больше никогда не нужно делать.

contentView.translatesAutoresizingMaskIntoConstraints = false
   
        NSLayoutConstraint.activate([
            contentView.leftAnchor.constraint(equalTo: leftAnchor),
            contentView.rightAnchor.constraint(equalTo: rightAnchor),
            contentView.topAnchor.constraint(equalTo: topAnchor),
            contentView.bottomAnchor.constraint(equalTo: bottomAnchor)
        ])


Уточнение получены. Продолжим.

Для начала уточним, как нам добиться динамического размера вьюхи в ширину и высоту. Приведу простой и наглядный пример. Создам вьюхи и помещу туда два лейбла. Пишу всё исключительно кодом, т.к. не использую сториборд, но думаю свести проблем не составит. И так:

5cab4bce2a9a6308684642.png
Код текстом
let v = UIView(frame: .zero)

    let l1 = UILabel(frame: .zero)
        l1.translatesAutoresizingMaskIntoConstraints = false
        l1.font = .systemFont(ofSize: 27.0)
        l1.text = "asdlas"

    v.addSubview(l1)
    NSLayoutConstraint.activate([
      l1.topAnchor.constraint(equalTo: v.topAnchor),
      l1.leadingAnchor.constraint(equalTo: v.leadingAnchor),
    ])
    l1.setContentHuggingPriority(.required, for: .vertical)
    l1.setContentCompressionResistancePriority(.required, for: .vertical)

    let trailing = l1.trailingAnchor.constraint(equalTo: v.trailingAnchor)
        trailing.priority = .defaultHigh
    NSLayoutConstraint.activate([
      trailing
    ])

    let l2 = UILabel(frame: .zero)
        l2.translatesAutoresizingMaskIntoConstraints = false
        l2.font = .systemFont(ofSize: 17.0)
        l2.text = ""

    v.addSubview(l2)
    NSLayoutConstraint.activate([
      l2.topAnchor.constraint(equalTo: l1.bottomAnchor),
      l2.leadingAnchor.constraint(equalTo: v.leadingAnchor),
    ])
    l2.setContentHuggingPriority(.required, for: .vertical)
    l2.setContentCompressionResistancePriority(.required, for: .vertical)

    let trailing_ = l2.trailingAnchor.constraint(equalTo: v.trailingAnchor)
        trailing_.priority = .defaultHigh
    let bottom = l2.bottomAnchor.constraint(equalTo: v.bottomAnchor)
        bottom.priority = .defaultHigh
    NSLayoutConstraint.activate([
      trailing_,
      bottom
    ])


    let s = v.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)

    view.addSubview(v)
    v.frame.size = s
    v.center = view.center
    v.layer.borderColor = UIColor.red.cgColor
    v.layer.borderWidth = 1.0


В принципе мы делаем обычные констрениты, задавая топ и лидинг. Примечателен тут другой момент, а именно трейлинг. Перед его заданием, мы обязательно установили св-ва сопротивление сжатию и сопротивления расстяжения обоим лейблам, давая тем самым системе понять, что хотим видеть лейбл ровно такой ширины, какой он есть на самом деле. После мы делаем констрейнт трейлинг и указываем ему приоритет немного ниже чем 1000(required). Кстати, быть может в сториборде приоритет снижать не придется. Проверьте. (Кажись система делаем это за вас)

С шириной всё готово. Смотрим на костреинты для высоты. Тут вполне обычно, начинаем с топ. После нижний лейбл крепится к боттом первого. И снова небольшая магия. Последний нижний констрейнт снова с пониженным приоритетом. Кстати, быть может в сториборде приоритет снижать не придется. Проверьте. (Кажись система делаем это за вас)

Если быть точнее снижая приоритет(по крайне мере если задавать их кодом) такой подход даёт понять системе, что высоту и ширину он прямиком и полностью должен расчитывать исходя из высоты и ширины его сабвьюх, а не назначить свои значения.

Поглядим на примеры:
Первая имеет меньший размер шрифта, но больше символов.
HC2w483.png
Первая имеет больший размер шрифта, но больше символов.
5MhP2nj.png
Первая имеет меньший размер шрифта, но меньше символов.
V00A7Qc.png
Первая имеет больший размер шрифта, но меньше символов.
zXH8glH.png

Как видимо размер вьюхи система посчитала вольностью верным. То есть мы динамически научились задавать высоту и ширину ячейки.

Поехали дальше.

Но для ячеек есть ещё одна небольшая проблема. Система лайаута коллекции имеет все нужные констреинты, но она ничего не знает о данных в этих ячеек. А ведь именно они задают ширину ячеейки. Для этого нам нужно в какой-то момент времени немного подсказать системе.

Воспользуемся методом:
5cab50033a6e2506454233.png
Код текстом
func preferredLayoutAttributesFitting(
    _ layoutAttributes: UICollectionViewLayoutAttributes
  ) -> UICollectionViewLayoutAttributes
  {
    let layoutAttributes = super.preferredLayoutAttributesFitting(layoutAttributes)

    let fittingSize = UIView.layoutFittingCompressedSize

    layoutAttributes.frame.size = systemLayoutSizeFitting(
      fittingSize,
      withHorizontalFittingPriority: .fittingSizeLevel,
      verticalFittingPriority: .fittingSizeLevel
    )

    return layoutAttributes
  }


Так как все констреинты у нас настроены, нам осталась попросить систему подсчитать нужные размеры и передать новые размеры системе лайаута коллекции. В этот момент кстати, все данные ячейка уже получила. (Текст для лейблов)

И малая ремарка, возможно в метод preferredLayoutAttributesFitting после вызова super. надо будет добавить вызов метода layoutIfNeeded(). А может и не нужно.

Итого:
Добавляем то, что в пункте 1.
Устанавливаем нужные констреинты.
Удаляем ваш код в пункте 3.
Добавляем метод preferredLayoutAttributesFitting
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы