UICollectionViewLayout с динамическими высотами - но НЕ с использованием схемы потока - PullRequest
1 голос
/ 05 октября 2019

Скажем, у вас есть UICollectionView с обычным пользовательским UICollectionViewLayout.

Так что это >>> NOT <<< макет потока - это обычный пользовательскийlayout. </p>

Пользовательские макеты тривиальны, при вызове prepare вы просто переходите к данным и размечаете каждый прямоугольник. Допустим, это коллекция с вертикальной прокруткой ...

override func prepare() {
    cache = []
    var y: CGFloat = 0
    let k = collectionView?.numberOfItems(inSection: 0) ?? 0
    // or indeed, just get that direct from your data

    for i in 0 ..< k {

        // say you have three cell types ...
        let h = ... depending on the cell type, say 100, 200 or 300

        let f = CGRect(
            origin: CGPoint(x: 0, y: y ),
            size: CGSize(width: screen width, height: h)
        )

        y += thatHeight
        y += your gap between cells

        cache.append( .. that one)
    }
}

В этом примере высота ячейки просто фиксирована для каждого, скажем, трех типов ячеек - все без проблем.

Обработка динамической ячейкивысоты если вы используете макет потока хорошо изучен и действительно относительно прост . ( Пример , также см. Много объяснений на www.)

Однако, что если вы хотите динамическую высоту ячейки с ( NON -потоком) совершенно обычным повседневным UICollectionViewLayout?

Где приблизительныйItemSize?

Насколько я могу судить, в UICollectionViewLayout нет НИКАКОГО оцененного понятияItemSize?

Так что, черт возьми, вы делаете?

Вы можете наивно просто - в приведенном выше коде - просто рассчитать конечные высоты каждой ячейки тем или иным способом (например, вычислить высоту любых текстовых блоков и т. Д.). Но это кажется совершенно неэффективным: ничто из общего представления не может быть нарисовано до тех пор, пока не будут рассчитаны целые сотни размеров ячеек. Вы бы вообще не использовали динамическую высоту iOS и ничто не было бы как раз вовремя.

Полагаю, вы могли бы запрограммировать всю систему "точно в срок" с нуля. (Итак, что-то вроде ... сделайте размер таблицы на самом деле только 1, вычислите вручную эту высоту, отправьте ее в представление коллекции, вычислите высоту элемента 2, отправьте ее вместе и т. Д.) Но это довольно неубедительно.

Есть ли какой-нибудь способ достичь динамической высоты ячеек с помощью пользовательского UICollectionViewLayout - НЕ макета потока?

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

Как я уже говорил выше, первая загадка - где ад - это понятие "предполагаемого размера" в (нормальном, не-потоке)) UICollectionViewLayout?

1 Ответ

1 голос
/ 08 октября 2019

Просто предупреждение: пользовательские макеты FAR от тривиальны, они могут заслуживать исследовательскую работу самостоятельно;)

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

  1. Во-первых, вам нужно внедрить preferredLayoutAttributesFitting(_:) в свои ячейки (или, в более общем случае, представления многократного использования). подкласс). Здесь вы можете использовать любые расчеты, которые вы хотите. Скорее всего, вы будете использовать автоматическое расположение с вашими ячейками: если это так, вам нужно будет добавить все подпредставления ячейки к ее contentView, ограничить их краями и затем вызвать systemLayoutSizeFitting(_:withHorizontalFittingPriority:verticalFittingPriority:) в рамках этого метода «предпочтительных атрибутов». Например, если вы хотите, чтобы размер ячейки изменялся по вертикали, а ограничение по горизонтали, вы должны написать:

    override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
    
                // Ensures that cell expands horizontally while adjusting itself vertically.
                let preferredSize = systemLayoutSizeFitting(layoutAttributes.size, withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel)
    
                layoutAttributes.size = preferredSize
                return layoutAttributes
            }
    
  2. После того, как ячейка запросит свои предпочтительные атрибуты, shouldInvalidateLayout(forPreferredLayoutAttributes:withOriginalAttributes:)на макете будет называться объект. Что важно, вы не можете просто набрать return true, так как система будет повторно запрашивать ячейку бесконечно. Это на самом деле очень умно, так как многие ячейки могут реагировать на изменения друг друга, поэтому именно компоновка в конечном итоге решает, выполнено ли это, удовлетворяя желания ячеек. Обычно для изменения размера вы пишете что-то вроде этого:

    override func shouldInvalidateLayout(forPreferredLayoutAttributes preferredAttributes: UICollectionViewLayoutAttributes, withOriginalAttributes originalAttributes: UICollectionViewLayoutAttributes) -> Bool {
    
                if preferredAttributes.size.height.rounded() != originalAttributes.size.height.rounded() {
                    return true
                }
                return false
            }
    
  3. Сразу после этого будет вызываться invalidationContext(forPreferredLayoutAttributes:withOriginalAttributes:). Обычно вы хотите настроить контекстный класс для хранения информации, специфичной для вашего макета. Одно важное, довольно не интуитивное предостережение заключается в том, что вы должны , а не вызывать context.invalidateItems(at:), потому что это приведет к тому, что макет аннулирует только тех элементов из предоставленных путей индекса, которые фактически видимы. Просто пропустите этот метод, чтобы макет запрашивал видимый прямоугольник.

    Однако! Вам нужно тщательно подумать, если вам нужно установить contentOffsetAdjustment и contentSizeAdjustment: если что-то изменится, ваш вид коллекции в целом, вероятно, уменьшится или расширится. Если вы не учитываете их, при прокрутке у вас будут прыжковые перезагрузки.

  4. Наконец, будет вызван invalidateLayout(with:). Это шаг, который предназначен для того, чтобы вы на самом деле отрегулировали высоту секций / строк, переместили что-то, на что повлияла ячейка изменения размера и т. Д. Если вы переопределите, вам нужно будет вызвать super.

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

...