Michael
Michael

Reputation: 51

Header in NSCollectionView is has wrong size

I have an ViewController with a NSCollectionView with Headers and Items. At a first glance everything looks fine:

NSCollectionView

As soon as I resize the window, one header becomes the size of an item – and the other headers disappear:

NSCollectionView with wrong header size

This is my code:

extension ViewController:NSCollectionViewDataSource {
    
    static let picItem = "PictureItem"
    static let headerItem = "HeaderItem"
    
    func numberOfSections(in collectionView: NSCollectionView) -> Int {
        return 3
    }
    
    func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
        return 15
    }
    
    func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
        let itemView = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: ViewController.picItem), for: indexPath)
        return itemView
    }
    
    func collectionView(_ collectionView: NSCollectionView, viewForSupplementaryElementOfKind kind: NSCollectionView.SupplementaryElementKind, at indexPath: IndexPath) -> NSView {
        let headerView = collectionView.makeSupplementaryView(ofKind: kind, withIdentifier: NSUserInterfaceItemIdentifier(rawValue: ViewController.headerItem), for: indexPath)
        return headerView
    }
}


extension ViewController:NSCollectionViewDelegateFlowLayout {
    
    func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> NSSize {
        return NSSize(width: 0, height: 20)
    }
}

The storyboard:

CollectionView in Storyboard

What's going on here? What did I miss?

Additional Code

HeaderItem.swift

class HeaderItem: NSCollectionViewItem {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do view setup here.
        
        view.wantsLayer = true
        view.layer?.backgroundColor = NSColor.green.cgColor
    }
    
}

HeaderItem.xib

HeaderItem.xib

The outlet view of the HeaderItem object is the CustomView.

CustomView Label

Upvotes: 0

Views: 649

Answers (1)

Sergei Sevriugin
Sergei Sevriugin

Reputation: 498

I think the best way is follow Apple guidelines for the new approach . First create UICollectionView Layout where specify all geometry params for header, cells and footers, for example:

import UIKit
import Combine
import Resolver

class DeliveryController: UIViewController {

var dataSource: UICollectionViewDiffableDataSource<Order, OrderItem>! = nil
    
    func createLayout() -> UICollectionViewLayout {
            
            let itemWidth: CGFloat = 140.0
            let itemHeight: CGFloat = 140.0
            let headerHeight: CGFloat = 140.0
            let footerHeight: CGFloat = 68.0
            
            let config = UICollectionViewCompositionalLayoutConfiguration()
            config.interSectionSpacing = 10.0
            
            let layout = UICollectionViewCompositionalLayout(sectionProvider: {
                (sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
                
                let order = self.orders[sectionIndex]
                
                let item = NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(
                    widthDimension: .absolute(itemWidth), heightDimension: .absolute(itemHeight)))
                
                item.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 0, bottom: 0, trailing: 10)
                
                let containerGroup = NSCollectionLayoutGroup.horizontal(
                    layoutSize: NSCollectionLayoutSize(widthDimension: .absolute(itemWidth), heightDimension: .estimated(itemHeight)), subitems: [item])
                
                let section = NSCollectionLayoutSection(group: containerGroup)
                
                section.orthogonalScrollingBehavior = UICollectionLayoutSectionOrthogonalScrollingBehavior.continuous
                
                let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem(
                    layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                                      heightDimension: .absolute(headerHeight)),
                    elementKind: DeliveryController.sectionHeaderElementKind,
                    alignment: .top)
                
                let sectionFooter = NSCollectionLayoutBoundarySupplementaryItem(
                    layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                                      heightDimension: .absolute(footerHeight)),
                    elementKind: DeliveryController.sectionFooterElementKind,
                    alignment: .bottom)
                
                if order.hasDriver {
                    
                    section.boundarySupplementaryItems = [sectionHeader, sectionFooter]
                } else {
                    
                    section.boundarySupplementaryItems = [sectionHeader]
                }
               
                return section
            
            }, configuration: config)
            
            
            return layout
        }

Then configureHierarchy using given layout:

func configureHierarchy(in view: UIView, top: UIView, bottom: UIView) {
        collectionView = UICollectionView(frame: .zero /* create with zero frame and use constarins after we adding collectionView as subview */, collectionViewLayout: createLayout())
        collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        collectionView.backgroundColor = .white
        
        view.addSubview(collectionView)
        
        collectionView.anchor(top: top.bottomAnchor, left: view.leftAnchor, bottom: bottom.topAnchor, right: view.rightAnchor, paddingTop: 8, paddingLeft: 0, paddingBottom: 2, paddingRight: 0)
        
        collectionView.delegate = self
        
        
    }

And then DataSource:

func configureDataSource() {
        
        let cellRegistration = UICollectionView.CellRegistration<OrderItemCell, OrderItem> { (cell, indexPath, item) in
            cell.item = item
        }
        
        dataSource = UICollectionViewDiffableDataSource<Order, OrderItem>(collectionView: collectionView) {
            (collectionView: UICollectionView, indexPath: IndexPath, item: OrderItem) -> UICollectionViewCell? in
            return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: item)
        }
        
        
        let headerRegistration = UICollectionView.SupplementaryRegistration
        <OrderHeaderView>(elementKind: "Header") {
            (supplementaryView, string, indexPath) in
            
            let order = self.orders[indexPath.section]
            supplementaryView.order = order
        }
        
        let footerRegistration = UICollectionView.SupplementaryRegistration
        <OrderFooterView>(elementKind: "Footer") {
            (supplementaryView, string, indexPath) in
            
            let order = self.orders[indexPath.section]
            supplementaryView.order = order
            supplementaryView.delegate = self
        }
        
        dataSource.supplementaryViewProvider = { (view, kind, index) in
            
            let order = self.orders[index.section]
            
            if kind == DeliveryController.sectionHeaderElementKind {
                
                return self.collectionView.dequeueConfiguredReusableSupplementary(using: headerRegistration, for: index)
            } else if order.hasDriver {
                
                return self.collectionView.dequeueConfiguredReusableSupplementary(using: footerRegistration, for: index)
            }
            
            return nil
        }
        
        var snapshot = NSDiffableDataSourceSnapshot<Order, OrderItem>()
        
        orders.forEach { order in
            snapshot.appendSections([order])
            snapshot.appendItems(order.items, toSection: order)
        }
        
        dataSource.apply(snapshot, animatingDifferences: false)
        
    }

}

Upvotes: 0

Related Questions