iOS - Как заставить UICollectionViewCell адаптировать свою высоту в соответствии с его содержимым?(содержащий UITableView) - PullRequest
0 голосов
/ 05 декабря 2018

Я не знаю, почему так сложно создавать ячейки, которые могут адаптироваться к его содержанию.Это не должно нуждаться в таком большом количестве кода, я все еще не понимаю, почему UIKit не может обработать это должным образом.

В любом случае, вот моя проблема ( Я отредактировал весь пост ):

У меня есть UICollectionViewCell, который содержит UITableView.

Вот мой sizeForItem метод:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout,
                        sizeForItemAt indexPath: IndexPath) -> CGSize {

        var cellWidth: CGFloat = collectionView.bounds.size.width 
        var cellHeight: CGFloat = 0

        let cellConfigurator = items[indexPath.item].cellConfigurator

        if type(of: cellConfigurator).reuseId == "MoonCollectionViewCell" {
            if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: type(of: cellConfigurator).reuseId, for: indexPath) as? MoonCollectionViewCell {
               cell.contentView.layoutIfNeeded()
               let size = cell.selfSizedTableView.intrinsicContentSize
               cellHeight = size.height
            }
        }

        return CGSize.init(width: cellWidth, height: cellHeight)
    }

sizeForItem вызывается раньше cellForItem, это причина layoutIfNeeded, потому что я не мог получитьправильный intrinsic content size.

Я удалил XIB, как предложено, и спроектировал мой UICollectionViewCell в раскадровке.

Вот мой UICollectionViewCell, спроектированный в раскадровке (только UITableViewCell разработан вФайл XIB)

Я только добавил UITableView в UICollectionViewCell.
Я хочу, чтобы UICollectionViewCell адаптировал свой размер в соответствии с высотойиз tableView.

Теперь вот мой tableView:

Я создал подкласс UITableView (из этой записи )

class SelfSizedTableView: UITableView {
    var maxHeight: CGFloat = UIScreen.main.bounds.size.height

    override func reloadData() {
        super.reloadData()
        self.invalidateIntrinsicContentSize()
        self.layoutIfNeeded()
    }

    override var intrinsicContentSize: CGSize {
        let height = min(contentSize.height, maxHeight)
        return CGSize(width: contentSize.width, height: height)
    }
}

Обратите внимание, что у меня есть disabled scrolling, у меня есть dynamic prototype для ячеек tableView, стиль: grouped.

РЕДАКТИРОВАТЬ: ПроверьтеМетод onfigure, он исходит из протокола, который я использовал для общей настройки всех моих UICollectionViewCell

func configure(data: [MoonImages]) {

        selfSizedTableView.register(UINib.init(nibName: "MoonTableViewCell", bundle: nil), forCellReuseIdentifier: "MoonTableViewCell")

        selfSizedTableView.delegate = self
        selfSizedTableView.dataSource = moonDataSource

        var frame = CGRect.zero
        frame.size.height = .leastNormalMagnitude
        selfSizedTableView.tableHeaderView = UIView(frame: frame)
        selfSizedTableView.tableFooterView = UIView(frame: frame)

        selfSizedTableView.maxHeight = 240.0
        selfSizedTableView.estimatedRowHeight = 40.0
        selfSizedTableView.rowHeight = UITableView.automaticDimension

        moonDataSource.data.addAndNotify(observer: self) { [weak self] in
            self?.selfSizedTableView.reloadData()
        }

        moonDataSource.data.value = data

    }

FYI dataSource - пользовательский источник данных с динамическим значением(Generics) и шаблон наблюдателя для перезагрузки collection / tableView при заданных данных.

У меня также появляется это предупреждение при запуске приложения.

[CollectionView] Попытка обновить информацию о макете была обнаружена еще в процессе вычисления макета (т. Е. Повторный входящий вызов).Это приведет к неожиданному поведению или падению.Это может произойти, если проход макета инициируется при вызове делегата.

Любые советы или рекомендации о том, как мне следует это делать?

Поскольку я сталкиваюсь со странным поведением,это как мой sizeForItem использовать случайные значения.Высота UICollectionViewCell не совпадает с высотой внутреннего размера UITableView .

Если в моем UITableView есть 2 строки, UICollectionView не всегда равенв этом размере.Я действительно не знаю, как этого добиться ...

Должен ли я invalideLayout?

Ответы [ 3 ]

0 голосов
/ 11 декабря 2018

Я пытался найти виновника в опубликованном коде, но, похоже, движущихся частей много.Итак, я попытаюсь дать некоторые подсказки, которые, надеюсь, могли бы помочь.

Теоретически (есть предостережение для iOS 12), самообладание UICollectionViewCells не должно быть трудным.По сути, вы можете установить collectionViewLayout.estimedItemSize на любое значение (предпочтительной является константа ниже), например:

(collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.estimatedItemSize = UICollectionViewFlowLayout.automaticSize

Затем вы должны убедиться, что ограничения в ячейках установлены таким образом, чтобыразмер себя;то есть автоматическое расположение может вычислить ширину и высоту ячейки.Вы предоставляете intrinsicContentSize tableView, и он оборачивается своим суперпредставлением со всех четырех концов, так что все должно быть в порядке.

Как только вы установите estimatedItemSize, как показано выше, вы не должны реализовыватьметод делегата, возвращающий размер:

func collectionView(_: UICollectionView, layout: UICollectionViewLayout, sizeForItemAt: IndexPath) -> CGSize

Краткое руководство можно найти здесь для дальнейшего использования: https://medium.com/@wasinwiwongsak/uicollectionview-with-autosizing-cell-using-autolayout-in-ios-9-10-84ab5cdf35a2

Как я уже говорил в теории, оно не должноЭто может быть сложно, но в iOS 12 автоматическое изменение размера ячейки кажется нарушенным, см. здесь В iOS 12, когда ячейки макета UICollectionView используют autolayout в nib

Если бы я находился в вашей позиции, я быначните заново, добавляя сложность шаг за шагом:

  • попробуйте реализовать ячейки с саморазмером, возможно, с помощью простого UIView и переопределения intrinsicContentSize;возможно, с помощью iOS 11.4 SDK для исключения проблем, связанных с iOS 12 (самый простой способ - загрузить последнюю версию Xcode 9 и работать оттуда);если это невозможно, исправьте iOS 12 на этом шаге
  • замените простое представление табличным представлением (которое также может иметь динамическое изменение размеров на см.)
  • выполняют поток данных перезагрузки представления таблицы, то есть динамическийфункция определения размера
  • если все в порядке, исправьте iOS 12 и перейдите на iOS 12

Надеюсь, это поможет.

Кстати, предупреждение в консоли, вероятно,из-за вызова layoutIfNeeded() в методе делегата.Он запускает немедленный проход макета, тогда как это делается для UICollectionView после сбора всех размеров.

0 голосов
/ 12 декабря 2018

Может быть, это не тот ответ, который вы хотели, но вот мои два цента.Для ваших конкретных требований, лучшее решение отходит от UITableView, и используйте UIStackView или ваш пользовательский вид контейнера.

Вот почему:

  1. UITableView являетсяподкласс UIScrollView, но, поскольку вы отключили его функцию прокрутки, вам не нужен UIScrollView.
  2. UITableView, который в основном используется для повторного использования ячеек, для повышения производительности и повышения структуризации кода.,Но так как вы увеличиваете его до размера контента, ни одна из ваших ячеек не используется повторно, поэтому возможности UITableView не используются.

Таким образом, на самом деле вы этого не делаетенужно, и вы не должны использовать UITableView или UIScrollView внутри UICollectionViewCell для ваших требований.

Если вы согласны с вышеуказанной частью, вот некоторые уроки из нашей практики:

  1. Мы всегда перемещаем большинство базовых представлений и логик кода, в основном сборку данных, в настраиваемое представление на основе UIView вместо непосредственного ввода UITableViewCell или UICollectionViewCell.Затем добавьте его к UITableViewCell или UICollectionViewCell contentView и настройте ограничения.С помощью этой структуры мы можем повторно использовать наш пользовательский вид в других сценариях.
  2. Для требований, аналогичных вашим, мы создадим фабричный класс для создания «строк», аналогичных тому, как вы создаете «ячейки» для * 1034.*, добавьте их в вертикальное UIStackView, создайте ограничения, определяющие ширину UIStackView.Автоматическое расположение позаботится обо всем остальном.
  3. Если вы используете UICollectionViewCell, чтобы вычислить требуемую высоту, внутри preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) функции вашей ячейки, вы можете использовать contentView.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel) для вычисления высоты,сделать некоторую проверку и вернуться.Также не забывайте отменять макет при изменении ширины UICollectionView.
0 голосов
/ 10 декабря 2018

Это действительно очень сложно, но я нашел способ решить эту проблему.Насколько я знаю, я получил это из приложения чата, в котором размеры пузырьков сообщений являются динамическими.

Вот и мы:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout,
                    sizeForItemAt indexPath: IndexPath) -> CGSize {

    // Minimum size
    let frame = CGRect(x: 0, y: 0, width: view.frame.width - 30, height: 0)
    let cell = MoonCollectionViewCell()

    // Fill it with the content it will have in the actual cell,
    // cell.content is just an example
    let cell.content = items[indexPath.item]
    cell.layoutIfNeeded()

    // Define the maximum size it can be
    let targetSize = CGSize(width: view.frame.width - 30, height: 240)
    let estimatedSize = cell.systemLayoutSizeFittingSize(tagetSize)

    return CGSize(width: view.frame.width - 30, height: estimatedSize.height)
}

Что он в основном делает, это определяет минимальный фрейми размер, который предназначен.Затем, вызывая systemLayoutSizeFittingSize, он изменяет размер ячейки до оптимального размера, но не больше, чем targetSize.

Настройте код в соответствии с вашими потребностями, но это должно работать.

...