使用UICollectionViewCompositionalLayout嵌套组时滚动异常求助
UICollectionViewCompositionalLayout 水平滚动网格布局显示异常问题
需求与当前问题
需要实现2行×x项的网格布局:每行是水平排列x个项目的组,两个行组嵌套在垂直组中,Section支持水平滚动,要求每行在视口中仅显示2个项目且能预览下一个项目。
当前已实现项目排列顺序如下:
| 1 | 2 | 3 |
| 4 | 5 |
但滚动查看第三个项目时无法完全显示,该行最后一个项目未为组增加宽度。
当前代码
let numberOfItemsOnTheFirstRow = Int(ceil(Double(favoritesCount) / 2)) let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(0.5), heightDimension: .fractionalHeight(1) ) let item = NSCollectionLayoutItem(layoutSize: itemSize) let groupSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(0.90), heightDimension: .fractionalHeight(0.5) ) let rowGroup: NSCollectionLayoutGroup if #available(iOS 16.0, *) { rowGroup = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, repeatingSubitem: item, count: numberOfItemsOnTheFirstRow) } else { rowGroup = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count:numberOfItemsOnTheFirstRow) } rowGroup.interItemSpacing = .fixed(16) let containerGroupSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), heightDimension: .estimated(290) ) let containerGroup = NSCollectionLayoutGroup.vertical(layoutSize: containerGroupSize, subitems: [rowGroup, rowGroup]) containerGroup.interItemSpacing = .fixed(16) let section = NSCollectionLayoutSection(group: containerGroup) section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 16, bottom: 16, trailing: 16) section.interGroupSpacing = 16 section.addingFavoritesSectionHeader() section.orthogonalScrollingBehavior = .continuousGroupLeadingBoundary return section
问题分析与修复方案
核心问题
- 行组宽度固定导致内容溢出:给
rowGroup设置.fractionalWidth(0.90)的固定比例宽度,当行内项目数量超过2个时,组宽度不会随项目数量扩展,超出部分被截断。 - 重复复用同一行组实例:直接将同一个
rowGroup添加两次到垂直组,第二行的项目数量应为favoritesCount - numberOfItemsOnTheFirstRow,复用会导致布局逻辑混乱。
修复后的代码
let favoritesCount = // 替换为你的实际数据数量 let numberOfItemsOnTheFirstRow = Int(ceil(Double(favoritesCount) / 2)) let numberOfItemsOnTheSecondRow = favoritesCount - numberOfItemsOnTheFirstRow // 定义Item尺寸:每个Item占行组宽度的一半 let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(0.5), heightDimension: .fractionalHeight(1) ) let item = NSCollectionLayoutItem(layoutSize: itemSize) // 将Item间的间距放到Item的内边距中,避免组间距计算冲突 item.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 16) // 封装行组创建方法,根据项目数量动态生成 func createRowGroup(with itemCount: Int) -> NSCollectionLayoutGroup { // 使用estimated宽度,让行组根据内部Item数量自动计算总宽度 let groupWidth = NSCollectionLayoutDimension.estimated( (UIScreen.main.bounds.width * 0.5 - 8) * CGFloat(itemCount) + 16 * CGFloat(itemCount - 1) ) let groupSize = NSCollectionLayoutSize( widthDimension: groupWidth, heightDimension: .fractionalHeight(0.5) ) if #available(iOS 16.0, *) { return NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, repeatingSubitem: item, count: itemCount) } else { return NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: itemCount) } } // 分别创建两行的组,对应各自的项目数量 let firstRowGroup = createRowGroup(with: numberOfItemsOnTheFirstRow) let secondRowGroup = createRowGroup(with: numberOfItemsOnTheSecondRow) // 垂直容器组使用estimated宽度,让Section适配内部行组的总宽度 let containerGroupSize = NSCollectionLayoutSize( widthDimension: .estimated(UIScreen.main.bounds.width), heightDimension: .estimated(290) ) let containerGroup = NSCollectionLayoutGroup.vertical(layoutSize: containerGroupSize, subitems: [firstRowGroup, secondRowGroup]) containerGroup.interItemSpacing = .fixed(16) // 配置Section let section = NSCollectionLayoutSection(group: containerGroup) section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 16, bottom: 16, trailing: 16) section.orthogonalScrollingBehavior = .continuousGroupLeadingBoundary section.addingFavoritesSectionHeader() return section
关键优化点
- 动态行组宽度:用
.estimated维度让行组根据Item数量和间距自动计算宽度,保证所有项目完整显示。 - 独立行组实例:分别创建两行的组,对应正确的项目数量,避免复用导致的布局异常。
- Item内边距替代组间距:将间距移到Item上,统一控制Item间的间隙,避免组内间距与宽度适配的冲突。
内容的提问来源于stack exchange,提问作者iDev
相关产品推荐
相关产品推荐

