iOS 组合布局(UICollectionViewCompositionalLayout)的实际应用案例

案例 1:电商 App 首页(多模块混合布局)

场景描述

电商首页通常是「顶部 Banner → 分类入口(2 行 4 列)→ 爆款推荐(1 行 2 列)→ 商品列表(2 列网格)」的组合,不同模块用不同布局规则,这是组合布局最典型的应用场景。

布局思路

  • 拆分 4 个 Section,分别对应 Banner、分类、爆款、商品;
  • 每个 Section 独立配置 Group/Item 尺寸,通过 contentInsets 控制间距;
  • Banner 用全屏宽度的 Item,分类用小尺寸网格,商品用标准 2 列网格。

核心代码(关键是多 Section 布局配置)

swift
 
 
 
 
 
func createHomeLayout() -> UICollectionViewCompositionalLayout {
    // 用闭包配置不同Section的布局
    return UICollectionViewCompositionalLayout { sectionIndex, _ -> NSCollectionLayoutSection? in
        switch sectionIndex {
        case 0: // 1. Banner区(全屏宽,固定高度)
            let itemSize = NSCollectionLayoutSize(
                widthDimension: .fractionalWidth(1.0),
                heightDimension: .absolute(200)
            )
            let item = NSCollectionLayoutItem(layoutSize: itemSize)
            let group = NSCollectionLayoutGroup.horizontal(layoutSize: itemSize, subitems: [item])
            let section = NSCollectionLayoutSection(group: group)
            section.contentInsets = .init(top: 10, leading: 0, bottom: 10, trailing: 0)
            return section
            
        case 1: // 2. 分类入口(2行4列,正方形Item)
            let itemSize = NSCollectionLayoutSize(
                widthDimension: .fractionalWidth(0.25),
                heightDimension: .fractionalHeight(1.0)
            )
            let item = NSCollectionLayoutItem(layoutSize: itemSize)
            item.contentInsets = .init(top: 5, leading: 5, bottom: 5, trailing: 5)
            
            let groupSize = NSCollectionLayoutSize(
                widthDimension: .fractionalWidth(1.0),
                heightDimension: .absolute(80) // 2行 → 每组高度80,对应单个Item高度40
            )
            let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
            let section = NSCollectionLayoutSection(group: group)
            // 2行:通过重复Group实现
            section.repeatBoundarySupplementaryItems = []
            return section
            
        case 2: // 3. 爆款推荐(1行2列,宽高比16:9)
            let itemSize = NSCollectionLayoutSize(
                widthDimension: .fractionalWidth(0.5),
                heightDimension: .fractionalHeight(1.0)
            )
            let item = NSCollectionLayoutItem(layoutSize: itemSize)
            item.contentInsets = .init(top: 5, leading: 5, bottom: 5, trailing: 5)
            
            let groupSize = NSCollectionLayoutSize(
                widthDimension: .fractionalWidth(1.0),
                heightDimension: .absolute(180) // 16:9 → 宽度屏宽,高度180
            )
            let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
            let section = NSCollectionLayoutSection(group: group)
            return section
            
        case 3: // 4. 商品列表(2列网格,自适应高度)
            let itemSize = NSCollectionLayoutSize(
                widthDimension: .fractionalWidth(0.5),
                heightDimension: .estimated(250) // 预估高度,适配不同商品卡片
            )
            let item = NSCollectionLayoutItem(layoutSize: itemSize)
            item.contentInsets = .init(top: 5, leading: 5, bottom: 5, trailing: 5)
            
            let groupSize = NSCollectionLayoutSize(
                widthDimension: .fractionalWidth(1.0),
                heightDimension: .estimated(250)
            )
            let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
            let section = NSCollectionLayoutSection(group: group)
            return section
            
        default:
            return nil
        }
    }
}
 

案例 2:社交 App 动态流(瀑布流 + 混合排版)

场景描述

朋友圈 / 小红书类动态流,每条动态包含「文字 + 1/3/4/9 张图片」,图片区域需要瀑布流效果(不同高度),文字区域自适应高度,整体布局灵活且不规则。

布局思路

  • 单个 Section 适配所有动态,通过 estimated 尺寸实现动态高度;
  • 图片组根据图片数量动态调整 Item 数量和尺寸(1 张全屏、3 张 1 行 3 列、9 张 3 行 3 列);
  • 文字区域作为 Supplementary View 嵌入,和图片区域组合成完整动态。

核心代码(瀑布流核心逻辑)

swift
 
 
 
 
 
func createFeedLayout() -> UICollectionViewCompositionalLayout {
    // 1. 文字区域(Supplementary View)
    let headerSize = NSCollectionLayoutSize(
        widthDimension: .fractionalWidth(1.0),
        heightDimension: .estimated(80) // 自适应文字高度
    )
    let header = NSCollectionLayoutBoundarySupplementaryItem(
        layoutSize: headerSize,
        elementKind: "textHeader",
        alignment: .top
    )
    
    // 2. 图片Item(根据数量动态调整尺寸)
    let itemSize = NSCollectionLayoutSize(
        widthDimension: .fractionalWidth(1.0/3), // 3列基础
        heightDimension: .fractionalHeight(1.0)
    )
    let item = NSCollectionLayoutItem(layoutSize: itemSize)
    item.contentInsets = .init(top: 2, leading: 2, bottom: 2, trailing: 2)
    
    // 3. 图片Group(瀑布流核心:预估高度)
    let groupSize = NSCollectionLayoutSize(
        widthDimension: .fractionalWidth(1.0),
        heightDimension: .estimated(200) // 图片高度随机,预估200
    )
    // 这里可根据图片数量动态生成subitems(比如1张时itemSize设为1.0)
    let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
    
    // 4. 整体Section
    let section = NSCollectionLayoutSection(group: group)
    section.boundarySupplementaryItems = [header] // 加入文字头部
    section.contentInsets = .init(top: 10, leading: 10, bottom: 10, trailing: 10)
    
    return UICollectionViewCompositionalLayout(section: section)
}

// 补充:在数据源中根据图片数量调整Item尺寸
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let feed = feeds[indexPath.item]
    switch feed.imageCount {
    case 1:
        return CGSize(width: collectionView.bounds.width - 20, height: CGFloat.random(in: 150...300)) // 随机高度实现瀑布流
    case 3,4:
        return CGSize(width: (collectionView.bounds.width - 30)/3, height: CGFloat.random(in: 80...150))
    case 9:
        return CGSize(width: (collectionView.bounds.width - 40)/3, height: (collectionView.bounds.width - 40)/3)
    default:
        return CGSize(width: (collectionView.bounds.width - 30)/2, height: CGFloat.random(in: 100...200))
    }
}
 

案例 3:资讯 App 列表(多样式混合布局)

场景描述

新闻 / 资讯 App 的列表页,包含「纯文字条目、左图右文、上图下文、多图条目」等多种样式,需要在同一个列表中无缝切换,且适配不同屏幕尺寸。

布局思路

  • 单个 Section,通过 Item 的 layoutSize 动态区分不同样式;
  • 左图右文:Group 水平排列(图片 Item + 文字 Item);
  • 上图下文:Group 垂直排列(图片 Item + 文字 Item);
  • 纯文字:直接用全屏宽的 Item。

核心代码(多样式布局)

swift
 
 
 
 
 
func createNewsLayout() -> UICollectionViewCompositionalLayout {
    return UICollectionViewCompositionalLayout { sectionIndex, _ -> NSCollectionLayoutSection? in
        // 通用配置:Section间距
        let sectionInsets = NSDirectionalEdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16)
        
        // 定义不同样式的Item/Group
        func createTextOnlyItem() -> NSCollectionLayoutItem {
            // 纯文字Item:全屏宽,自适应高度
            let size = NSCollectionLayoutSize(
                widthDimension: .fractionalWidth(1.0),
                heightDimension: .estimated(100)
            )
            let item = NSCollectionLayoutItem(layoutSize: size)
            return item
        }
        
        func createLeftImageRightTextGroup() -> NSCollectionLayoutGroup {
            // 左图(1/3宽)+ 右文(2/3宽)
            let imageItemSize = NSCollectionLayoutSize(
                widthDimension: .fractionalWidth(1/3),
                heightDimension: .fractionalHeight(1.0)
            )
            let imageItem = NSCollectionLayoutItem(layoutSize: imageItemSize)
            
            let textItemSize = NSCollectionLayoutSize(
                widthDimension: .fractionalWidth(2/3),
                heightDimension: .fractionalHeight(1.0)
            )
            let textItem = NSCollectionLayoutItem(layoutSize: textItemSize)
            textItem.contentInsets = .init(top: 0, leading: 10, bottom: 0, trailing: 0)
            
            // Group:固定高度120
            let groupSize = NSCollectionLayoutSize(
                widthDimension: .fractionalWidth(1.0),
                heightDimension: .absolute(120)
            )
            return NSCollectionLayoutGroup.horizontal(
                layoutSize: groupSize,
                subitems: [imageItem, textItem]
            )
        }
        
        func createTopImageBottomTextGroup() -> NSCollectionLayoutGroup {
            // 上图(全屏宽)+ 下文(自适应高度)
            let imageItemSize = NSCollectionLayoutSize(
                widthDimension: .fractionalWidth(1.0),
                heightDimension: .absolute(180)
            )
            let imageItem = NSCollectionLayoutItem(layoutSize: imageItemSize)
            
            let textItemSize = NSCollectionLayoutSize(
                widthDimension: .fractionalWidth(1.0),
                heightDimension: .estimated(80)
            )
            let textItem = NSCollectionLayoutItem(layoutSize: textItemSize)
            textItem.contentInsets = .init(top: 10, leading: 0, bottom: 0, trailing: 0)
            
            // Group:垂直排列
            let groupSize = NSCollectionLayoutSize(
                widthDimension: .fractionalWidth(1.0),
                heightDimension: .estimated(260)
            )
            return NSCollectionLayoutGroup.vertical(
                layoutSize: groupSize,
                subitems: [imageItem, textItem]
            )
        }
        
        // 根据数据类型选择布局
        let newsType = newsList[sectionIndex].type
        var group: NSCollectionLayoutGroup!
        switch newsType {
        case .textOnly:
            let item = createTextOnlyItem()
            group = NSCollectionLayoutGroup.horizontal(layoutSize: item.layoutSize, subitems: [item])
        case .leftImageRightText:
            group = createLeftImageRightTextGroup()
        case .topImageBottomText:
            group = createTopImageBottomTextGroup()
        }
        
        let section = NSCollectionLayoutSection(group: group)
        section.contentInsets = sectionInsets
        return section
    }
}
 

案例 4:工具类 App 设置页(分组列表 + 网格混合)

场景描述

设置页通常是「分组标题 → 单行设置项(文字 + 开关)→ 多行功能入口(网格)」,比如系统设置的 “通用” 页面,既有列表又有网格,布局规整且分层清晰。

布局思路

  • 每个设置分组对应一个 Section,Section 头部是分组标题;
  • 单行设置项:Group 水平排列(文字 Item + 开关 Item);
  • 多行功能入口:Group 水平排列多个小尺寸 Item,重复多行。

核心代码(关键是分组标题 + 单行布局)

swift
 
 
 
 
 
func createSettingsLayout() -> UICollectionViewCompositionalLayout {
    // 1. 分组标题(Supplementary View)
    let headerSize = NSCollectionLayoutSize(
        widthDimension: .fractionalWidth(1.0),
        heightDimension: .absolute(44)
    )
    let header = NSCollectionLayoutBoundarySupplementaryItem(
        layoutSize: headerSize,
        elementKind: UICollectionView.elementKindSectionHeader,
        alignment: .top
    )
    
    return UICollectionViewCompositionalLayout { sectionIndex, _ -> NSCollectionLayoutSection? in
        let settingGroup = settings[sectionIndex]
        switch settingGroup.type {
        case .singleRow: // 单行设置项(文字+开关)
            let textItemSize = NSCollectionLayoutSize(
                widthDimension: .fractionalWidth(0.8),
                heightDimension: .fractionalHeight(1.0)
            )
            let textItem = NSCollectionLayoutItem(layoutSize: textItemSize)
            
            let switchItemSize = NSCollectionLayoutSize(
                widthDimension: .fractionalWidth(0.2),
                heightDimension: .fractionalHeight(1.0)
            )
            let switchItem = NSCollectionLayoutItem(layoutSize: switchItemSize)
            
            let groupSize = NSCollectionLayoutSize(
                widthDimension: .fractionalWidth(1.0),
                heightDimension: .absolute(44) // 标准行高
            )
            let group = NSCollectionLayoutGroup.horizontal(
                layoutSize: groupSize,
                subitems: [textItem, switchItem]
            )
            
            let section = NSCollectionLayoutSection(group: group)
            section.boundarySupplementaryItems = [header] // 加入分组标题
            return section
            
        case .grid: // 网格功能入口(3列)
            let itemSize = NSCollectionLayoutSize(
                widthDimension: .fractionalWidth(1/3),
                heightDimension: .fractionalHeight(1.0)
            )
            let item = NSCollectionLayoutItem(layoutSize: itemSize)
            
            let groupSize = NSCollectionLayoutSize(
                widthDimension: .fractionalWidth(1.0),
                heightDimension: .absolute(88) // 2行 → 每行44
            )
            let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
            
            let section = NSCollectionLayoutSection(group: group)
            section.boundarySupplementaryItems = [header]
            // 重复Group实现多行
            section.orthogonalScrollingBehavior = .none // 禁止横向滚动
            return section
        }
    }
}
 

总结

  1. 组合布局的核心是分层拆解:将复杂界面拆分为「Item-Group-Section」,不同模块用不同尺寸 / 排列方式,再组合起来;
  2. 实际开发中优先用 fractional(比例)和 estimated(预估)尺寸,适配不同屏幕;
  3. 多样式布局通过「Section 区分」或「动态调整 Item/Group 尺寸」实现,Supplementary View 可灵活添加头部 / 尾部元素;
  4. 瀑布流的核心是给 Item 设置随机 / 动态高度,配合 estimated 尺寸让布局自适应。
posted @ 2025-12-19 15:36  爱吃牛腩  阅读(0)  评论(0)    收藏  举报