Самостоятельное изменение размера ячеек с помощью UICollectionViewCompositionalLayout

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

Проблема

Представление коллекции ведет себя неожиданно, когда контроллер представления представлен с использованием значения по умолчанию pageSheet style на iOS 13+.

  1. При перемещении листа вверх может казаться, что размер ячеек меняется, как в ячейке с надписью "Исправить" ниже:

  1. При вытягивании листа вверх содержимое может сместиться по горизонтали. Иногда клетки тоже могут исчезнуть:

Вопрос

Есть ли способ исправить это поведение, продолжая использовать UICollectionViewCompositionalLayout и pageSheet стиль презентации?

Резюме кода

Код довольно прост. Всего 3 класса, которые можно сбросить вViewController.swift файл с использованием шаблона проекта приложения для единого просмотра в Xcode.

  1. А UICollectionViewCell класс называется Cell. В ячейке естьUILabel и отменяет sizeThatFits(_:).
  2. А UIViewController называется ViewController используется только для представления BugViewController.
  3. BugViewController, который настраивает источник данных и представляет представление коллекции. Вот где возникает проблема.

Код

import UIKit

// MARK: - Cell -

final class Cell: UICollectionViewCell {
    static let reuseIdentifier = "Cell"

    lazy var label: UILabel = {
        let label = UILabel()
        label.textAlignment = .center
        label.frame.size = contentView.bounds.size
        label.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        return label
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        contentView.addSubview(label)
        contentView.backgroundColor = .tertiarySystemFill
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func sizeThatFits(_ size: CGSize) -> CGSize {
        .init(width: label.sizeThatFits(size).width + 32, height: 32)
    }
}

// MARK: - ViewController -

final class ViewController: UIViewController {
    private let button: UIButton = {
        let button = UIButton(type: .system)
        button.setTitle("Tap Me!".uppercased(), for: .normal)
        button.addTarget(self, action: #selector(presentBugViewController), for: .touchUpInside)
        button.sizeToFit()
        return button
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(button)
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        button.center = view.center
    }

    @objc func presentBugViewController() {
        present(BugViewController(), animated: true)
    }
}

// MARK: - BugViewController -

final class BugViewController: UIViewController {
    private let models = [
        "Better Call Saul",
        "Mad Men",
        "Rectify",
        "Tiger King: Murder, Mayhem, and Madness",
        "Master of None",
        "BoJack Horseman"
    ]

    private lazy var collectionView: UICollectionView = {
        let collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: createCollectionViewLayout())
        collectionView.register(Cell.self, forCellWithReuseIdentifier: Cell.reuseIdentifier)
        collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        collectionView.contentInset.top = 44
        collectionView.backgroundColor = .white
        return collectionView
    }()

    private lazy var dataSource = UICollectionViewDiffableDataSource<Int, String>(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Cell.reuseIdentifier, for: indexPath) as? Cell else { fatalError() }
        cell.label.text = itemIdentifier
        return cell
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(collectionView)

        var snapshot = NSDiffableDataSourceSnapshot<Int, String>()
        snapshot.appendSections([0])
        snapshot.appendItems(models)
        dataSource.apply(snapshot)
    }

    private func createCollectionViewLayout() -> UICollectionViewCompositionalLayout {
        let layoutSize = NSCollectionLayoutSize.init(
            widthDimension: .estimated(200),
            heightDimension: .absolute(32)
        )

        let section = NSCollectionLayoutSection(group:
            .horizontal(
                layoutSize: layoutSize,
                subitems: [.init(layoutSize: layoutSize)]
            )
        )
        section.interGroupSpacing = 8
        section.orthogonalScrollingBehavior = .continuous

        return .init(section: section)
    }
}

Заметки

  • Представление коллекции в моем приложении на самом деле имеет много разделов и прокручивается по вертикали. Вот почему я использую представление коллекции с вертикальной прокруткой и раздел сorthogonalScrollingBehavior в примере кода.

Неудачные попытки

  • Я пробовал использовать ограничения Auto Layout вместо sizeThatFits(_:).
  • Я пробовал не использовать UICollectionViewDiffableDataSource.

Обходные пути

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

0 ответов

Другие вопросы по тегам