You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为横向UICollectionView的分组背景视图设置正确高度

解决UICollectionView横向滚动时分组背景视图显示异常问题

问题场景

使用UICollectionView的Compositional Layout实现横向滚动的分组列表,发现分组背景视图的高度从最后一个item位置开始填充;但将CollectionView设置为纵向滚动时,背景显示完全正常。

异常显示效果

异常显示效果

正常显示效果(纵向滚动)

正常显示效果(纵向滚动)

问题代码

class SectionDecorationViewController: UIViewController {

    static let sectionBackgroundDecorationElementKind = "section-background-element-kind"

    var currentSnapshot: NSDiffableDataSourceSnapshot<Int, Int>! = nil
    var dataSource: UICollectionViewDiffableDataSource<Int, Int>! = nil
    var collectionView: UICollectionView! = nil
    var currentSelection: Bool = false

    override func viewDidLoad() {
        super.viewDidLoad()
        navigationItem.title = "Section Background Decoration View"
        configureHierarchy()
        configureDataSource()
    }
}

extension SectionDecorationViewController {
    /// - Tag: Background
    func createLayout() -> UICollectionViewLayout {

        let sectionProvider = { [weak self] (sectionIndex: Int,
                                 layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in



            guard let self = self else { return nil }

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

            // Get the number of items in the section
            let itemCount = self.currentSnapshot.numberOfItems(inSection: sectionIndex)

            // Custom group height based on the number of items
            let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                                   heightDimension: .absolute(CGFloat(itemCount) * 44))
            let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitems: [item])

            let section = NSCollectionLayoutSection(group: group)
            section.orthogonalScrollingBehavior = .paging
            section.interGroupSpacing = 0 // Remove inter-group spacing
            section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0) // Remove content insets

            // Add the background decoration item
            let sectionBackgroundDecoration = NSCollectionLayoutDecorationItem.background(
                elementKind: SectionDecorationViewController.sectionBackgroundDecorationElementKind)
            section.decorationItems = [sectionBackgroundDecoration]
            return section
        }
        
        let config = UICollectionViewCompositionalLayoutConfiguration()
        config.scrollDirection = .horizontal
        config.interSectionSpacing = 0
        let layout = UICollectionViewCompositionalLayout(
            sectionProvider: sectionProvider, configuration: config)
        layout.register(
            SectionBackgroundDecorationView.self,
            forDecorationViewOfKind: SectionDecorationViewController.sectionBackgroundDecorationElementKind)
        return layout
    }
}

extension SectionDecorationViewController {
    func configureHierarchy() {
        collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: createLayout())
        collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        collectionView.backgroundColor = .systemBackground
    
        view.addSubview(collectionView)
        collectionView.delegate = self
    }
    func configureDataSource() {
        
        let cellRegistration = UICollectionView.CellRegistration<ListCell, Int> { (cell, indexPath, identifier) in
            // Populate the cell with our item description.

            cell.label.text = "\(indexPath.section),\(indexPath.item)"

        }
        
        dataSource = UICollectionViewDiffableDataSource<Int, Int>(collectionView: collectionView) {
            (collectionView: UICollectionView, indexPath: IndexPath, identifier: Int) -> UICollectionViewCell? in
            // Return the cell.
            return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: identifier)
        }

        // initial data
        let itemsPerSection = 5
        let sections = Array(0..<5)
        currentSnapshot = NSDiffableDataSourceSnapshot<Int, Int>()
        var itemOffset = 0
        sections.forEach {
            currentSnapshot.appendSections([$0])
            currentSnapshot.appendItems(Array(itemOffset..<itemOffset + itemsPerSection))
            itemOffset += itemsPerSection
        }
        dataSource.apply(currentSnapshot, animatingDifferences: false)
    }
}

问题原因

当给section设置orthogonalScrollingBehavior = .paging开启横向分页滚动后,每个section的高度会和CollectionView的容器高度保持一致。但你现在是手动用itemCount * 44设置group的绝对高度,这就导致背景视图的尺寸计算和实际section高度不匹配,从而出现背景从最后一个item位置才开始填充的异常。

修复方案

把group的高度维度改成.fractionalHeight(1.0),让group自动填满整个section的高度,这样背景视图就能正确覆盖整个分组区域。同时可以移除依赖currentSnapshot获取itemCount的代码,因为不再需要手动计算高度。

修改后的createLayout方法

func createLayout() -> UICollectionViewLayout {
    let sectionProvider = { (sectionIndex: Int,
                             layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                              heightDimension: .absolute(44))
        let item = NSCollectionLayoutItem(layoutSize: itemSize)

        // 让group高度自动填满section高度
        let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                               heightDimension: .fractionalHeight(1.0))
        let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitems: [item])

        let section = NSCollectionLayoutSection(group: group)
        section.orthogonalScrollingBehavior = .paging
        section.interGroupSpacing = 0
        section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)

        let sectionBackgroundDecoration = NSCollectionLayoutDecorationItem.background(
            elementKind: SectionDecorationViewController.sectionBackgroundDecorationElementKind)
        section.decorationItems = [sectionBackgroundDecoration]
        return section
    }
    
    let config = UICollectionViewCompositionalLayoutConfiguration()
    config.scrollDirection = .horizontal
    config.interSectionSpacing = 0
    let layout = UICollectionViewCompositionalLayout(
        sectionProvider: sectionProvider, configuration: config)
    layout.register(
        SectionBackgroundDecorationView.self,
        forDecorationViewOfKind: SectionDecorationViewController.sectionBackgroundDecorationElementKind)
    return layout
}

内容的提问来源于stack exchange,提问作者harshalb

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 10:10:58