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
相关产品推荐
相关产品推荐

