📰

CompositionalLayoutのヘッダ領域を完全に無くす方法

2022/09/06に公開

次のようなコードで、2つのセクションを持つCompositionalLayoutを作ります。

enum Section: Int {
    case groupA
    case groupB
}

struct Item: Identifiable, Hashable {
    let id: UUID = UUID()
}

class ViewController: UIViewController {
    let configuration: UICollectionLayoutListConfiguration = {
        var configuration = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
        configuration.headerMode = .none
        return configuration
    }()
    lazy var listLayout: UICollectionViewCompositionalLayout = .list(using: configuration)
    lazy var collectionView = UICollectionView(
        frame: .null,
        collectionViewLayout: listLayout
    )
    lazy var dataSource = UICollectionViewDiffableDataSource<Section, Item>(
        collectionView: collectionView,
        cellProvider: { [unowned self] (collectionView, indexPath, item) in
            collectionView.dequeueConfiguredReusableCell(
                using: cellRegistration,
                for: indexPath,
                item: item
            )
        }
    )
    let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, Item>(handler: { (cell, indexPath, item) in
        var contentConfiguration = cell.defaultContentConfiguration()
        contentConfiguration.text = "\(indexPath)"
        cell.contentConfiguration = contentConfiguration
    })
    
    override func loadView() {
        view = collectionView
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        _ = dataSource
        
        var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
        snapshot.appendSections([.groupA, .groupB])
        snapshot.appendItems([.init()], toSection: .groupA)
        snapshot.appendItems([.init()], toSection: .groupB)
        dataSource.apply(snapshot)
    }
}

すると、headerModenoneであるにも関わらずセクションの間が微妙に空いてしまいます。

つまり、headerModeはヘッダ領域を完全に無くすのではなく余白は適度に開けつつ何も表示しないオプションであることが分かります。
このヘッダ領域を完全に無くすには、supplementaryViewProviderを使って自分でヘッダビューを提供し、そのサイズを0にします。

configuration.headerMode = .supplementary
...
let headerRegistration = UICollectionView.SupplementaryRegistration<UICollectionReusableView>(
  elementKind: UICollectionView.elementKindSectionHeader,
  handler: { (supplementaryView, elementKind, indexPath) in
    supplementaryView.frame.size.height = 0
  }
)
...
dataSource.supplementaryViewProvider = { [unowned self] (collectionView, elementKind, indexPath) in
  collectionView.dequeueConfiguredReusableSupplementary(
    using: headerRegistration,
    for: indexPath
  )
}

これで完全にヘッダ領域を無くすことができました。

Discussion