'UICollectionViewCompositionalLayout header misalignment

I have created a custom layout based on UICollectionViewCompositionalLayout that presents each section's items in a panel or card...

Panel without a header

...here the blue area is a NSCollectionLayoutDecorationItem that is passed to the section via its decorationItems property. This background item has its own content insets that inset it from the collection view bounds (the grey area) and the section has its own content insets to inset it within the panel. The purple section is 3 full width items (cells).

This all works as expected.

However, when I try to add a header to each section using boundarySupplementaryItems, the header (red) appears to ignore the section's content insets, but the section does size itself as though it's properly positioned...

Panel with a header

...the header pins itself to the top of the section, but the section height increases by the height of the header + the insets.

Here is the code I'm using to generate the layout...

let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)

let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(44.0))
let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitems: [item])

let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(44.0))
let headerItem = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: "header", alignment: .top)

let panelBackgroundItem = NSCollectionLayoutDecorationItem.background(elementKind: "PanelBackground")
panelBackgroundItem.contentInsets = NSDirectionalEdgeInsets(top: 4.0, leading: 8.0, bottom: 4.0, trailing: 8.0)

let section = NSCollectionLayoutSection(group: group)
section.boundarySupplementaryItems = [headerItem]
section.contentInsets = NSDirectionalEdgeInsets(top: 11.0, leading: 15.0, bottom: 11.0, trailing: 15.0)
section.decorationItems = [panelBackgroundItem]

let configuration = UICollectionViewCompositionalLayoutConfiguration()
configuration.interSectionSpacing = 4.0

let layout = UICollectionViewCompositionalLayout(section: section, configuration: configuration)
layout.register(PanelBackgroundView.self, forDecorationViewOfKind: "PanelBackground")

Can anyone advise on how I can get the header to position itself properly – just above the first item in the section, and respecting the content insets that have been set?



Solution 1:[1]

One way to work around, you can set contentInset.top to zero and using UICollectionViewCompositionalLayoutConfiguration.interSectionSpacing. Then the sectionHeader gonna be right above the first cell, and we also can configure the spacing among sections.

spacing among section using configuration

Solution 2:[2]

@Rafael, I post the answer here because the comment doesn't allow enough characters. This is taken from WWDC 2019's advanced collectionView layout :

func createLayout() -> UICollectionViewLayout {
    let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0))
    let item = NSCollectionLayoutItem(layoutSize: itemSize)
    
    let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(44))
    let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
    
    let section = NSCollectionLayoutSection(group: group)

    
    let header = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(44)), elementKind: SectionHeadersFootersViewController.sectionHeaderElementKind, alignment: .top)
    header.contentInsets = NSDirectionalEdgeInsets(top: 20, leading: 0, bottom: 20, trailing: 0)
    let footer = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(44)), elementKind: SectionHeadersFootersViewController.sectionFooterElementKind, alignment: .bottom)
    section.boundarySupplementaryItems = [header,footer]
    section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10)


    let config = UICollectionViewCompositionalLayoutConfiguration()
    config.interSectionSpacing = 10
    
    let layout = UICollectionViewCompositionalLayout(section: section, configuration: config)
    return layout
}

Result

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Mihozil
Solution 2