IOS 10/11 UICollectionViewFlowLayout с использованием UICollectionViewFlowLayoutAutomaticSize приводит к уменьшению нижнего колонтитула

Итак, это интересная проблема, которую мы обнаружили с помощью UICollectionViewFlowLayout на iOS 10 (по-прежнему проблема 11) и с помощью UICollectionViewFlowLayoutAutomaticSize для оцененногоItemSize.

Мы обнаружили, что использование UICollectionViewFlowLayoutAutomaticSize для оценочного результата в примере ItemSize в дополнительном представлении нижнего колонтитула, плавающего над нижними немногими ячейками. (Красный/Розовый - это заголовок, зеленый - нижний колонтитул.)

UICollectionViewFlowLayoutAutomaticSize проблема введите описание изображения здесь

Вот код VC примерного приложения:

import UIKit

class ViewController: UIViewController {

    // MARK: Properties

    let texts: [String] = [
        "This is some text",
        "This is some more text",
        "This is even more text"
    ]

    // MARK: Outlets

    @IBOutlet var collectionView: UICollectionView! {
        didSet {
            self.collectionView.backgroundColor = .orange
        }
    }

    // MARK: Lifecycle

    override func viewDidLoad() {

        super.viewDidLoad()

        // Layout
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .vertical
        if #available(iOS 10.0, *) {
            layout.estimatedItemSize = UICollectionViewFlowLayoutAutomaticSize
        } else {
            layout.estimatedItemSize = CGSize(width: self.collectionView.bounds.width, height: 50)
        }
        self.collectionView.collectionViewLayout = layout

        // Register Cells
        self.collectionView.register(UINib(nibName: "TextCell", bundle: nil), forCellWithReuseIdentifier: String(describing: TextCell.self))
        self.collectionView.register(UINib(nibName: "SectionHeader", bundle: nil), forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: String(describing: SectionHeader.self))
        self.collectionView.register(UINib(nibName: "SectionFooter", bundle: nil), forSupplementaryViewOfKind: UICollectionElementKindSectionFooter, withReuseIdentifier: String(describing: SectionFooter.self))

        self.collectionView.reloadData()
    }
}

// MARK: - UICollectionViewDelegateFlowLayout Methods

extension ViewController: UICollectionViewDelegateFlowLayout {

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {

        return CGSize(width: self.collectionView.bounds.width, height: 90)
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {

        return CGSize(width: self.collectionView.bounds.width, height: 90)
    }
}

// MARK: - UICollectionViewDataSource Methods

extension ViewController: UICollectionViewDataSource {

    public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {

        return self.texts.count
    }

    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }

    public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: TextCell.self), for: indexPath)

        if let textCell = cell as? TextCell {

            let text = self.texts[indexPath.row]
            textCell.configure(text: text)
        }

        return cell
    }

    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {

        switch kind {
        case UICollectionElementKindSectionHeader:

            return collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: String(describing: SectionHeader.self), for: indexPath)

        case UICollectionElementKindSectionFooter:

            return collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: String(describing: SectionFooter.self), for: indexPath)

        default:
            return UICollectionReusableView()
        }
    }
}

// MARK: - UICollectionViewDelegate Methods

extension ViewController: UICollectionViewDelegate {

}

Кто-нибудь смог получить UICollectionViewFlowLayoutAutomaticSize для работы с iOS 10 с дополнительными представлениями верхнего и нижнего колонтитулов? Если я добавлю размер в sampleItemSize, то он работает, но я хочу знать, есть ли ошибка в использовании новой функции iOS 10 или если я неправильно ее использую.

Ошибка, поданная с Apple, имеет идентификатор: 28843116

ОБНОВЛЕНИЕ. Это все еще кажется проблемой в 10.3 Beta 1

ОБНОВЛЕНИЕ 2. Это все еще кажется проблемой в iOS 11 Beta 1

Ответы

Ответ 1

UICollectionViewFlowLayout очень хорошо поддерживает автоматический макет для ячеек, НО, он не поддерживает его для дополнительных просмотров. Каждый раз, когда auto layout code обновляет ячейку кадра, он ничего не делает с верхними и нижними колонтитулами. Поэтому вам нужно сообщить макет, что он должен аннулировать верхние и нижние колонтитулы. И есть способ для этого!

Вы должны переопределить UICollectionViewLayout func invalidationContext(forPreferredLayoutAttributes: UICollectionViewLayoutAttributes, withOriginalAttributes: UICollectionViewLayoutAttributes) и выполнить в нем недопустимое недооценку.

Пример:

override open func invalidationContext(forPreferredLayoutAttributes preferred: UICollectionViewLayoutAttributes,
        withOriginalAttributes original: UICollectionViewLayoutAttributes)
                -> UICollectionViewLayoutInvalidationContext {
    let context: UICollectionViewLayoutInvalidationContext = super.invalidationContext(
            forPreferredLayoutAttributes: preferred,
            withOriginalAttributes: original
    )

    let indexPath = preferred.indexPath

    if indexPath.item == 0 {
        context.invalidateSupplementaryElements(ofKind: UICollectionElementKindSectionHeader, at: [indexPath])
    }

    return context
}

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

Ответ 2

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

используйте методы referenceSizeForHeaderInSection и referenceSizeForFooterInSection делегирования, чтобы явно указать размер заголовка и нижнего колонтитула.