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

iOS16及以上如何实现CollectionView组合布局的双列布局?

iOS 16+ 实现UICollectionView双列布局的正确方案

问题描述

我正在将嵌套了栈视图、CollectionView、TableView等的复杂滚动视图代码,转换为单一个使用UICollectionViewCompositionalLayout的CollectionView,但在通过count参数实现双列布局时遇到了问题。

为了测试,我参考了Apple《Implementing Modern Collection Views》示例项目中“Create a Column Layout”的代码:

extension TwoColumnViewController {
    /// - Tag: TwoColumn
    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, subitem: item, count: 2)
        let spacing = CGFloat(10)
        group.interItemSpacing = .fixed(spacing)

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

        let layout = UICollectionViewCompositionalLayout(section: section)
        return layout
    }
}

示例中的代码能正常显示双列布局,但在iOS 16中,horizontal(layoutSize:subitem:count:)方法已被弃用;替换为horizontal(layoutSize:repeatingSubitem:count:)后,出现了单元格超出屏幕且无法正常横向滚动的布局异常。

解决方法

问题根源在于新旧方法的逻辑差异:旧方法会自动按count平分group宽度并适配间距,而新的repeatingSubitem:count:会严格按照item设置的尺寸重复排列,不会自动调整item宽度。因此需要手动设置item的宽度比例,确保多个item(含间距)刚好填满group宽度。

方案一:调整item的宽度比例

通过计算item占group的宽度比例,配合固定间距实现双列:

extension TwoColumnViewController {
    /// - Tag: TwoColumn_iOS16
    func createLayout() -> UICollectionViewLayout {
        let spacing = CGFloat(10)
        // 计算每个item的宽度比例:(group总宽度 - 间距) / 列数,转换为fractionalWidth
        let itemWidthRatio = (1 - spacing / view.bounds.width) / 2
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(itemWidthRatio),
                                              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, repeatingSubitem: item, count: 2)
        group.interItemSpacing = .fixed(spacing)

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

        let layout = UICollectionViewCompositionalLayout(section: section)
        return layout
    }
}

方案二:手动定义子item并设置内边距

通过创建两个item,分别设置内边距来控制间距,避免比例计算:

extension TwoColumnViewController {
    func createLayout() -> UICollectionViewLayout {
        let spacing = CGFloat(10)
        // 每个item占group宽度的50%
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5),
                                              heightDimension: .fractionalHeight(1.0))
        
        // 第一个item设置右侧内边距实现间距
        let leftItem = NSCollectionLayoutItem(layoutSize: itemSize)
        leftItem.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: spacing)
        
        // 第二个item不需要右侧内边距
        let rightItem = NSCollectionLayoutItem(layoutSize: itemSize)

        let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                               heightDimension: .absolute(44))
        // 直接传入两个item组成group
        let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [leftItem, rightItem])

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

        let layout = UICollectionViewCompositionalLayout(section: section)
        return layout
    }
}

关键说明

  • 旧方法horizontal(layoutSize:subitem:count:)会自动调整item的宽度,让count个item填满group宽度并分配间距;
  • 新方法horizontal(layoutSize:repeatingSubitem:count:)严格遵循item的尺寸设置,因此必须手动确保item总宽度(含间距)不超过group宽度,否则会出现布局异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 20:14:51