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

使用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

问题分析与修复方案

核心问题

  1. 行组宽度固定导致内容溢出:给rowGroup设置.fractionalWidth(0.90)的固定比例宽度,当行内项目数量超过2个时,组宽度不会随项目数量扩展,超出部分被截断。
  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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 03:23:14