案例 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 尺寸讓佈局自適應。