NSCollectionView 中的标题大小错误

问题描述

我有一个带有带有标题和项目的 NSCollectionView 的 ViewController。乍一看,一切都很好:

NSCollectionView

一旦我调整窗口大小,一个标题就会变成一个项目的大小——而其他标题就会消失:

NSCollectionView with wrong header size

这是我的代码

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)
    }
}

故事板:

CollectionView in Storyboard

这是怎么回事?我错过了什么?

附加代码

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

HeaderItem 对象的出口视图是 CustomView。

CustomView

Label

解决方法

我认为最好的方法是遵循 Apple guidelines 的新方法。首先创建 UICollectionView 布局,其中指定页眉、单元格和页脚的所有几何参数,例如:

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
        }

然后使用给定的布局配置层次结构:

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
        
        
    }

然后是数据源:

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,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)
        
    }

}