单Section双Group的Compositional Layout为何仅沿用首个Group尺寸?
问题:UICollectionView Compositional Layout 分组分页滚动布局异常
需求说明
- 需实现两组元素的Compositional Layout:
- 第一组:2个Item A,各占屏幕宽度50%,整体占满屏幕宽度
- 第二组:1个Item B,占满屏幕宽度
- 支持分页滚动,滚动结束后仅显示完整的一组
期望布局示意图
[Item A][Item A]|[ Item B ]
(|代表屏幕边缘)
实际错误效果
[Item A][Item A]|[Item B]
Item B仅占屏幕宽度50%,不符合预期
错误实现代码
UICollectionViewCompositionalLayout { sectionIndex, layoutEnvironment in let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)) let planItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5), heightDimension: .fractionalHeight(1)) let planItem = NSCollectionLayoutItem(layoutSize: planItemSize) let planGroup = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: planItem, count: 2) let formItem = NSCollectionLayoutItem(layoutSize: groupSize) let formGroup = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: formItem, count: 1) let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [planGroup, formGroup]) let section = NSCollectionLayoutSection(group: group) section.orthogonalScrollingBehavior = .groupPaging return section }
问题原因
你将planGroup和formGroup嵌套进了一个宽度为.fractionalWidth(1)的水平父组中,导致两个子组被迫平分屏幕宽度,各占50%——Item B所在的formGroup只能拿到父组分配的50%宽度,最终显示为屏幕宽度的一半。
解决方案
要让每个分组独立占据一个屏幕宽度,需直接将两个分组作为section的独立单元,同时明确设置每个分组的宽度为屏幕宽度。
兼容iOS 15及以下的实现
UICollectionViewCompositionalLayout { sectionIndex, layoutEnvironment in // 获取容器实际宽度 let containerWidth = layoutEnvironment.container.effectiveContentSize.width // 第一组:两个Item A,各占50%屏幕宽度 let planItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5), heightDimension: .fractionalHeight(1)) let planItem = NSCollectionLayoutItem(layoutSize: planItemSize) let planGroupSize = NSCollectionLayoutSize(widthDimension: .absolute(containerWidth), heightDimension: .fractionalHeight(1)) let planGroup = NSCollectionLayoutGroup.horizontal(layoutSize: planGroupSize, subitem: planItem, count: 2) // 第二组:一个Item B,占满屏幕宽度 let formItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)) let formItem = NSCollectionLayoutItem(layoutSize: formItemSize) let formGroupSize = NSCollectionLayoutSize(widthDimension: .absolute(containerWidth), heightDimension: .fractionalHeight(1)) let formGroup = NSCollectionLayoutGroup.horizontal(layoutSize: formGroupSize, subitem: formItem, count: 1) // 创建组合分组,宽度为两个屏幕宽度 let combinedGroup = NSCollectionLayoutGroup.horizontal( layoutSize: NSCollectionLayoutSize(widthDimension: .absolute(containerWidth * 2), heightDimension: .fractionalHeight(1)), subitems: [planGroup, formGroup] ) let section = NSCollectionLayoutSection(group: combinedGroup) section.orthogonalScrollingBehavior = .groupPaging return section }
iOS 16+ 简化实现
利用NSCollectionLayoutSection(groups:)直接传入多个分组:
UICollectionViewCompositionalLayout { sectionIndex, layoutEnvironment in let containerWidth = layoutEnvironment.container.effectiveContentSize.width // 第一组 let planItem = NSCollectionLayoutItem(layoutSize: .init(widthDimension: .fractionalWidth(0.5), heightDimension: .fractionalHeight(1))) let planGroup = NSCollectionLayoutGroup.horizontal( layoutSize: .init(widthDimension: .absolute(containerWidth), heightDimension: .fractionalHeight(1)), subitem: planItem, count: 2 ) // 第二组 let formItem = NSCollectionLayoutItem(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1))) let formGroup = NSCollectionLayoutGroup.horizontal( layoutSize: .init(widthDimension: .absolute(containerWidth), heightDimension: .fractionalHeight(1)), subitem: formItem, count: 1 ) let section = NSCollectionLayoutSection(groups: [planGroup, formGroup]) section.orthogonalScrollingBehavior = .groupPaging return section }
关键说明
- 每个分组的宽度设置为
.absolute(containerWidth),确保分组刚好占满一个屏幕宽度 .groupPaging滚动行为保证滚动停止时,屏幕仅显示完整的一组- 避免将两个分组嵌套进同一屏幕宽度的父组,防止宽度被平分
内容的提问来源于stack exchange,提问作者stackunderflow
相关产品推荐
相关产品推荐

