2

我一直在尝试创建一个 UICollectionView 标头,该标头会粘贴在我的集合视图之上。我正在使用 UICollectionViewCompositionalLayout。

我尝试了多种方法:使用单元格,使用部分标题并尝试使用插入和偏移量来正确定位它相对于我的内容......甚至在集合视图的顶部添加一个视图来监听集合视图的滚动视图的 contentOffset 以将自身定位在正确的位置。但这些方法都不是令人满意的。他们都觉得自己像个黑客。

我一直在做一些研究,显然你必须对 UICollectionViewLayout 进行子类化,这非常乏味,而且对于只有一个标题似乎有点矫枉过正,但它对整个集合视图来说是全局的。

4

1 回答 1

9

TL;博士

UICollectionViewCompositionalLayout有一个configuration可以通过创建UICollectionViewCompositionalLayoutConfiguration对象来设置的属性。这个对象有一些非常好的和有用的功能,比如boundarySupplementaryItems属性。

从文档:

与整个布局的边界边缘相关联的补充项数组,例如全局页眉和页脚

答对了。设置此属性并在您的数据源中进行必要的接线,您应该有您的全局标头。

代码示例

在这里,我在布局中声明了一个全局标题。标头是视觉效果视图中的分段控件,但您的可以是UICollectionReusableView.

enum SectionLayoutKind: Int, CaseIterable {
    case description
}

private var collectionView: UICollectionView! = nil

override func viewDidLoad() {
    super.viewDidLoad()

    collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: createLayout())
}

static func descriptionSection() -> NSCollectionLayoutSection {
    // Instantiate and return a `NSCollectionLayoutSection` object.
}

func createLayout() -> UICollectionViewLayout {
    let layout = UICollectionViewCompositionalLayout {
        (sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
        // Create your section
        // add supplementaries such as header and footers that are relative to the section…
        guard let layoutKind = SectionLayoutKind(rawValue: sectionIndex) else { return nil }
        
        let section: NSCollectionLayoutSection
        switch layoutKind {
        case .description:
            section = Self.descriptionSection()
        }
        
        return section
    }
    
    /*
     ✨ Magic starts HERE:
    */
    let globalHeaderSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(44))
    Constants.HeaderKind.globalSegmentedControl, alignment: .top)
    let globalHeader = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: globalHeaderSize, elementKind: Constants.HeaderKind.space, alignment: .top)
    // Set true or false depending on the desired behavior
    globalHeader.pinToVisibleBounds = true
    
    let config = UICollectionViewCompositionalLayoutConfiguration()
    /*
     If you want to do spacing between sections.
     That's another big thing this config object does.
     If you try to define section spacing at the section level with insets,
     the spacing is between the items and the standard headers.
    */
    config.interSectionSpacing = 20
    config.boundarySupplementaryItems = [globalHeader]
    layout.configuration = config
    /*
     End of magic. ✨
    */
    
    return layout
}

struct Constants {
    struct HeaderKind {
        static let space = "SpaceCollectionReusableView"
        static let globalSegmentedControl = "segmentedControlHeader"
    }
}

数据源部分的补充代码:

let globalHeaderRegistration = UICollectionView.SupplementaryRegistration<SegmentedControlReusableView>(elementKind: Constants.HeaderKind.globalSegmentedControl) { (header, elementKind, indexPath) in
    // Opportunity to further configure the header
    header.segmentedControl.addTarget(self, action: #selector(self.onSegmentedControlValueChanged(_:)), for: .valueChanged)
}

dataSource.supplementaryViewProvider = { (view, kind, indexPath) in
    if kind == Constants.HeaderKind.globalSegmentedControl {
        return self.collectionView.dequeueConfiguredReusableSupplementary(using: globalHeaderRegistration, for: indexPath)
    } else {
        // return another registration object
    }
}
于 2020-09-14T08:42:40.157 回答