본문 바로가기
UIKit

UICompositional Layout에 관하여

by iOS 개린이 2023. 8. 21.

UICompositional Layout

Compositional Layout은 컬렉션 뷰 레이아웃의 한 유형이다. 

이는 기존의 단순한 리스트나 그리드 뷰를 넘어서 훨씬 더 복잡하고 다양한 레이아웃을 가능하게 한다.

 

UICompositional Layout의 구성요소

https://velog.io/@sopt_official/iOS1

Compositional Layout은 Item, Group, Section, Layout 으로 구성되어 있다.

 

1. Layout

NSCollectionLayout 클래스의 인스턴스로, 최종적으로 여러 섹션을 묶어 전체 컬렉션 뷰의 레이아웃을 정의한다.

 

2. Section

여러 그룹을 묶는 역할을 하며, NSCollectionLayoutSection 클래스의 인스턴스로 표현된다.

섹션은 그룹뿐만 아니라 Header, Footer 등의 보조 뷰를 가질 수 있으며, 그룹의 배열을 담고 있다.

 

3. Group

그룹은 여러 아이템을 묶는 역할을 하며, NSCollectionLayoutGroup 클래스의 인스턴스로 표현된다.

수평, 수직 또는 사용자 지정 방향으로 정렬될 수 있으며, 다른 그룹을 포함하는 중첩 구조도 가능하다.

 

4. Item

아이템은 컬렉션 뷰의 개별 셀을 나타내며, NSCollectionLayoutItem 클래스의 인스턴스로 표현된다.

크기, 콘텐츠, 간격 등의 속성을 정의한다.

 

 

Item 생성하기

let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)

 

group이 여러 item을 묶는 역할을 한다고 했다. 따라서 item은 group 안에 존재하면서, item은 group과의 비율로 나타내게 된다.

 

위 코드에서는 item의 width는 group의 width의 100% 길이,

item의 height는 group의 height의 100% 길이로 지정했다.

 

NSCollectionLayoutSize 정의

@available(iOS 13.0, *)
@MainActor open class NSCollectionLayoutSize : NSObject, NSCopying {

    public convenience init(widthDimension width: NSCollectionLayoutDimension, heightDimension height: NSCollectionLayoutDimension)

    
    open var widthDimension: NSCollectionLayoutDimension { get }

    open var heightDimension: NSCollectionLayoutDimension { get }
}

 

'NSCollectionLayoutSize' 는 item, group, section의 크기를 정의하기 위한 클래스이다.

init 메서드나 프로퍼티를 보면, 'NSCollectionLayoutDimension' 를 타입으로 받고 있다.

 

NSCollectionLayoutDimension 정의

@available(iOS 13.0, *)
@MainActor open class NSCollectionLayoutDimension : NSObject, NSCopying {

    // dimension is computed as a fraction of the width of the containing group
    open class func fractionalWidth(_ fractionalWidth: CGFloat) -> Self

    
    // dimension is computed as a fraction of the height of the containing group
    open class func fractionalHeight(_ fractionalHeight: CGFloat) -> Self

    
    // dimension with an absolute point value
    open class func absolute(_ absoluteDimension: CGFloat) -> Self

    
    // dimension is estimated with a point value. Actual size will be determined when the content is rendered.
    open class func estimated(_ estimatedDimension: CGFloat) -> Self

    
    open var isFractionalWidth: Bool { get }

    open var isFractionalHeight: Bool { get }

    open var isAbsolute: Bool { get }

    open var isEstimated: Bool { get }

    open var dimension: CGFloat { get }
}

 

'NSCollectionLayoutDimension' 클래스는 Compositional Layout 에서 크기를 추상화하는 데 사용된다.

이 클래스를 통해 아이템, 그룹, 섹션의 너비와 높이를 다양한 방식으로 정의할 수 있다.

 

1. fractionalWidth

컨테이너 그룹의 Width 비율에 따라 크기를 지정한다. 예를 들어 '0.5' 를 전달하면 그룹 Width의 50%로 계산된다.

 

2. fractionalHeight

컨테이너 그룹의 Height 비율에 따라 크기를 지정한다.

 

3. absolute

절대값으로 크기를 지정한다. 포인트 단위의 고정된 크기이다.

 

4. estimated

예상 크기로 지정한다. 실제 크기는 렌더링될 때 결정된다.

 

 

 

Group 생성하기

let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(44))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])

 

item을 가지고 Group을 구성하는 코드이다.

 

Group의 width는 Section의 width의 100% 길이이며,

height는 CGFloat(44) 크기이다.

 

 

NSCollectionLayoutGroup의 정의

@available(iOS 13.0, *)
@MainActor open class NSCollectionLayoutGroup : NSCollectionLayoutItem, NSCopying {

    // Specifies a group that will repeat items until available horizontal space is exhausted.
    //   note: any remaining space after laying out items can be apportioned among flexible interItemSpacing definitions
    open class func horizontal(layoutSize: NSCollectionLayoutSize, subitems: [NSCollectionLayoutItem]) -> Self

    
    // Specifies a group that will repeat items until available vertical space is exhausted.
    //   note: any remaining space after laying out items can be apportioned among flexible interItemSpacing definitions
    open class func vertical(layoutSize: NSCollectionLayoutSize, subitems: [NSCollectionLayoutItem]) -> Self

    
    // Specifies a custom group with client-specified frames.
    //   During layout, the itemProvider will be called with the group's current geometry provided via the NSCollectionLayoutEnvironment supplied.
    //   The coordinate space for returned frames should be {0,0} relative to this group's geometry.
    //   Custom groups can be nested arbitrarily inside other groups.
    open class func custom(layoutSize: NSCollectionLayoutSize, itemProvider: @escaping NSCollectionLayoutGroupCustomItemProvider) -> Self
}

 

생성 메서드 

1. horizontal

수평 방향으로 아이템을 나열하는 그룹을 만드는 메서드이다. 

'layouySize' 는 그룹의 크기를 지정하며, 'subItems' 는 그룹에 포함될 아이템의 배열을 나타낸다.

 

2. vertical

수직 방향으로 아이템을 나열하는 그룹을 만드는 메서드이다.

horizontal과 마찬가지로 'layouySize' 는 그룹의 크기를 지정하며, 'subItems' 는 그룹에 포함될 아이템의 배열을 나타낸다.

 

3. custom

사용자 지정 그룹을 만드는 메서드이다. 

'layouySize' 는 그룹의 크기를 지정하며, 'itemProvider' 클로저를 통해 사용자 지정 레이아웃을 제공할 수 있다.

 

 

 

Section 생성하기

let section = NSCollectionLayoutSection(group: group)

 

section은 'NSCollectionLayoutSection' 의 인스턴스를 생성하며, group을 받는다.

 

 

NSCollectionLayoutSection의 정의

@available(iOS 13.0, *)
@MainActor open class NSCollectionLayoutSection : NSObject, NSCopying {

    public convenience init(group: NSCollectionLayoutGroup)

    
    open var contentInsets: NSDirectionalEdgeInsets

    open var interGroupSpacing: CGFloat
    
    
    // default is .none
    open var orthogonalScrollingBehavior: UICollectionLayoutSectionOrthogonalScrollingBehavior

    
    // Supplementaries associated with the boundary edges of the section
    open var boundarySupplementaryItems: [NSCollectionLayoutBoundarySupplementaryItem]

    
    // decoration views anchored to the section's geometry (e.g. background decoration view)
    open var decorationItems: [NSCollectionLayoutDecorationItem]
}

 

1. 컨텐츠 여백과 간격

-contentInset

섹션의 컨텐츠 주위에 적용할 여백을 정의한다.

 

-interGroupSpacing

섹션 내의 연속적인 그룹 사이의 간격을 정의한다.

 

 

2. 스크롤 동작

-orthogonalScrollingBehavior

섹션의 수평 스크롤 동작을 정의한다. 예를 들어 수직으로 스크롤되는 컬렉션 뷰 내에서 수평으로 스크롤되는 섹션을 만들 수 있다.

 

3. 장식 뷰

-boundarySupplementaryItems

섹션의 경계에 연관된 보충 뷰를 정의한다. 헤더나 푸터와 같은 역할을 한다.

 

-decorationItems

섹션에 고정된 장식 뷰를 정의한다.

 

 

 

Compositional Layout 생성하기

let layout = UICollectionViewCompositionalLayout(section: section)

 

UICollectionViewCompositionalLayout 객체를 생성하며, section을 받는다. 

section의 내부에는 group, item, supplementary views, decoration views 등이 있을 수 있다.

 

 

UICollectionViewCompositionalLayout의 정의

@available(iOS 13.0, *)
@MainActor open class UICollectionViewCompositionalLayout : UICollectionViewLayout {

    public init(section: NSCollectionLayoutSection)

    public init(section: NSCollectionLayoutSection, configuration: UICollectionViewCompositionalLayoutConfiguration)

    
    public init(sectionProvider: @escaping UICollectionViewCompositionalLayoutSectionProvider)

    public init(sectionProvider: @escaping UICollectionViewCompositionalLayoutSectionProvider, configuration: UICollectionViewCompositionalLayoutConfiguration)

    
    // Setting this property will invalidate the layout immediately to affect any changes
    //    Note: any changes made to properties directly will have no effect.
    @NSCopying open var configuration: UICollectionViewCompositionalLayoutConfiguration
}

 

 

1. public init(section: NSCollectionLayoutSection)

단일 섹션으로 레이아웃을 초기화한다. 

각 섹션은 하나의 그룹을 포함할 수 있으며, 그룹 내부에는 여러 아이템이 존재할 수 있다.

 

2. public init(section: NSCollectionLayoutSection, configuration: UICollectionViewCompositionalLayoutConfiguration)

섹션과 레이아웃 Configuration을 사용하여 초기화한다.

Configuration을 통해 섹션 간의 간격, 스크롤 방향 등을 지정할 수 있다.

 

3. public init(sectionProvider: @escaping UICollectionViewCompositionalLayoutSectionProvider)

다수의 섹션을 가지고 각 섹션들의 레이아웃을 동적으로 구성해야 할 때 사용된다.

sectionProvider 클로저는 각 섹션의 레이아웃을 반환하며, 이를 통해 각 섹션의 레이아웃을 동적으로 구성할 수 있다.

즉, 서로 다른 섹션에 대해 다른 레이아웃을 적용할 수 있다.

 

4. public init(sectionProvider: @escaping UICollectionViewCompositionalLayoutSectionProvider, configuration: UICollectionViewCompositionalLayoutConfiguration)

다수의 섹션에 대한 레이아웃을 동적으로 구성할 때 사용되며, 추가로 섹션 간 간격, 스크롤 방향 등 전체 레이아웃에 대한 세부 설정도 가능하다.

 

 

 

UICompositional Layout 실전

 

위 그림과 같이 좌우로 스크롤 되는 레이아웃을 만들어보자.

 

private static func createLayout() -> UICollectionViewLayout {
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0))
        let item = NSCollectionLayoutItem(layoutSize: itemSize)
        item.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 5)
        
        let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(80))
        let header = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: UICollectionView.elementKindSectionHeader, alignment: .top)
        
        let groupSize = NSCollectionLayoutSize(widthDimension: .absolute(200), heightDimension: .absolute(170))
        let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
        
        let section = NSCollectionLayoutSection(group: group)
        section.boundarySupplementaryItems = [header]
        section.orthogonalScrollingBehavior = .continuous
        section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10)
        
        return UICollectionViewCompositionalLayout(section: section)
    }

 

1. Item 설정

NSCollectionLayoutSize를 통해 아이템의 사이즈를 설정했다.

위 예시에서는 그룹의 width, height와 동일한 비율로 지정되었다.

'contentInsets' 은 각 아이템에 대한 여백을 설정한다.

 

2. header 설정

headerView는 'UICollectionReusableView' 로 하나 만들었다.

'headerSize' 는 헤더의 크기를 설정한다. 

 

헤더 뷰는 'NSCollectionLayoutBoundarySupplementaryItem' 클래스의 인스턴스를 생성하여 정의한다.

'layoutSize' 는 헤더 뷰의 크기를 정의하고, 'elementKind' 는 헤더 뷰의 종류를 정의하며, 우리는 'UICollectionView.elementKindSectionHeader' 를 통해 섹션 헤더로 사용됨을 설정했다.

마지막으로 'alignment' 는 헤더 뷰의 정렬을 지정하는 값으로, .top 으로 설정하여 섹션의 상단에 정렬되도록 했다.

 

따라서 해당 코드는 섹션 헤더의 크기, 종류, 위치를 지정하여, 해당 섹션의 상단에 배치되는 헤더 뷰를 생성하도록 한다.

 

3. Group 설정

아이템과 동일하게 'NSCollectionLayoutSize' 를 통해 그룹의 크기를 설정한다.

아이템이 수평으로 배열되는 그룹을 생성하며, 설정한 레이아웃과 아이템을 전달한다.

 

4. Section 설정

섹션을 생성하며, 위에서 정의한 group과 header를 배치한다.

 

'orthogonalScrollingBehavior' 를 통해 섹션 내의 스크롤 동작을 설정한다.

이것을 설정해주지 않으면 디폴트 값인 .none으로 처리되며, 이 경우에는 컬렉션 뷰의 기본 스크롤 동작을 따른다.

 

section의 'contentInsets' 는 섹션의 컨텐츠 주변에 여백을 설정한다.

 

 

UICompositional Layout 실전2

 

이번엔 두 개의 section을 가지고 있는 Compositional Layout을 구성해보자.

 

위에서 다수의 section을 가지면서 각각 동적인 Layout을 구현하기 위해서는 'UICollectionViewCompositionalLayout' 의 'public init(sectionProvider: @escaping UICollectionViewCompositionalLayoutSectionProvider)' 메서드를 사용해야 한다고 했다.

 

먼저 'UICollectionViewCompositionalLayoutSectionProvider' 의 정의는 다음과 같다.

public typealias UICollectionViewCompositionalLayoutSectionProvider = (Int, NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection?

 

클로저의 매개변수 'Int' 는 섹션의 인덱스를 나타낸다. 

그리고 'NSCollectionLayoutEnvironment' 는 컬렉션 뷰의 현재 상태와 관련된 정보를 포함하며, 컨테이너의 크기 등을 조회할 수 있다. 클로저의 반환 값은 'NSCollectionLayoutSection?' 으로 우리가 정의한 섹션을 반환해주면 된다.

 

따라서 해당 초기화 메서드를 가지고, 두 개의 섹션을 가지고 있는 CompositionalLayout을 구성해보자.

private func createCompositionalLayout() -> UICollectionViewLayout {
        
        let layout = UICollectionViewCompositionalLayout { [weak self] sectionIndex, evvironment in
            
            if sectionIndex == 0 {
                
                let section = self?.createFirstSectionLayout()
                return section
            }
            
            if sectionIndex == 1 {
                let section = self?.createSecondSectionLayout()
                return section
            }
            
            return nil
        }
        
        return layout
    }

 

sectionIndex에 따라 다른 레이아웃 섹션을 반환하도록 구현했다.

 

각 섹션을 구현하는 메서드

private func createFirstSectionLayout() -> NSCollectionLayoutSection {
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0))
        let item = NSCollectionLayoutItem(layoutSize: itemSize)
        item.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 5)
        
        let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(60))
        let header = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: UICollectionView.elementKindSectionHeader, alignment: .top)
        
        let groupSize = NSCollectionLayoutSize(widthDimension: .absolute(200), heightDimension: .absolute(170))
        let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
        
        let section = NSCollectionLayoutSection(group: group)
        section.boundarySupplementaryItems = [header]
        section.orthogonalScrollingBehavior = .continuous
        section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 10, bottom: 50, trailing: 10)
        
        return section
    }
    
    
    private func createSecondSectionLayout() -> NSCollectionLayoutSection {
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0))
        let item = NSCollectionLayoutItem(layoutSize: itemSize)
        item.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 10, trailing: 0)
        
        let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(60))
        let header = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: UICollectionView.elementKindSectionHeader, alignment: .top)
        
        let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(170))
        let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitems: [item])
        
        let section = NSCollectionLayoutSection(group: group)
        section.boundarySupplementaryItems = [header]
        section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10)
        
        return section
    }

 

 

첫 번째 섹션과 두 번째 섹션을 만드는 코드는 다음과 같이 만들었다. 

섹션을 만드는 코드에 대한 설명은 위에서 여러 번 설명했기 때문에 따로 설명이 필요없다!

 

DecorationView 추가하기

1. UICollectionReusableView 의 서브 클래스로 DecorationView를 정의한다.

final class DecorationView: UICollectionReusableView {
      static let identifier = "DecorationView"
      ...
}

 

2. Decoration Item을 생성한다.

let decorationItem = NSCollectionLayoutDecorationItem.background(elementKind: DecorationView.identifier)

 

3. Section에 Decoration Item을 추가한다.

section.decorationItems = [decorationItem]

 

4. Compositional Layout에 DecorationView를 등록한다.

let layout = UICollectionViewCompositionalLayout { ... }
layout.register(DecorationView.self, forDecorationViewOfKind: DecorationView.identifier)

 

 

여기서 주의할 점은 섹션의 헤더 뷰인 'Supplementary View' 는 UICollectionView 내에서 register() 하고,

'Decoration View' 는 UICollectionViewCompositionalLayout  내에서 register() 해야 한다는 것이다.

 

 

 

UICompositional Layout 장점

1. 간단함

Compostional Layout을 사용하지 않고, 실전과 같은 뷰를 구성하기 위해서는 테이블 뷰 내에 컬렉션 뷰가 들어가거나, 스크롤 뷰 내에 스택 뷰, 테이블 뷰 등이 들어가는 구조로 뷰를 만들어야 한다. 그리고 해당 작업은 굉장히 복잡하다. 

하지만 Compostional Layout은 하나의 컬렉션 뷰를 가지고 다양한 레이아웃을 구성할 수 있기 때문에 굉장히 간단해진다.

 

2. 성능

위에서 설명했듯이, 여러 뷰를 가지고 레이아웃을 구성하면 사용자에게 필요하지 않은 뷰까지 모두 그려야 한다. 이것은 필요하지 않은 뷰에 대한 메모리 할당을 가지고 있는 것이다. 하지만 Compositional Layout을 통해 필요한 뷰만 계산하고, 적용할 수 있으며, 재사용 큐를 통해 뷰를 효율적으로 관리할 수 있다.

 

 

 

전체 코드

import UIKit
import RxSwift
import RxCocoa
import FlexLayout
import PinLayout

final class CompositionalViewController: UIViewController {
    
    lazy var collectionView : UICollectionView = {
        let layout = createCompositionalLayout()
        
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.register(FirstSectionCollectionViewCell.self, forCellWithReuseIdentifier: FirstSectionCollectionViewCell.identifier)
        collectionView.register(SecondSectionCollectionViewCell.self, forCellWithReuseIdentifier: SecondSectionCollectionViewCell.identifier)
        collectionView.register(HeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: HeaderView.identifier)
        
        return collectionView
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        setupSubViews()
    }
    
    private func setupSubViews() {
        view.backgroundColor = .white
        
        view.addSubview(collectionView)
        collectionView.backgroundColor = .clear
        collectionView.dataSource = self
        collectionView.delegate = self
        collectionView.pin.all()
    }
    
    private func createCompositionalLayout() -> UICollectionViewLayout {
        
        let layout = UICollectionViewCompositionalLayout { [weak self] sectionIndex, evvironment in
            
            if sectionIndex == 0 {
                
                let section = self?.createFirstSectionLayout()
                return section
            }
            
            if sectionIndex == 1 {
                let section = self?.createSecondSectionLayout()
                return section
            }
            
            return nil
        }
        
        layout.register(DecorationView.self, forDecorationViewOfKind: DecorationView.identifier)

        return layout
    }
    
    private func createFirstSectionLayout() -> NSCollectionLayoutSection {
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0))
        let item = NSCollectionLayoutItem(layoutSize: itemSize)
        item.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 5)
        
        let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(60))
        let header = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: UICollectionView.elementKindSectionHeader, alignment: .top)
        
        let groupSize = NSCollectionLayoutSize(widthDimension: .absolute(200), heightDimension: .absolute(170))
        let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
        
        let section = NSCollectionLayoutSection(group: group)
        section.boundarySupplementaryItems = [header]
        section.orthogonalScrollingBehavior = .continuous
        section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 10, bottom: 50, trailing: 10)
        
        return section
    }
    
    
    private func createSecondSectionLayout() -> NSCollectionLayoutSection {
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0))
        let item = NSCollectionLayoutItem(layoutSize: itemSize)
        item.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 10, trailing: 0)
        
        let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(150))
        let header = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: UICollectionView.elementKindSectionHeader, alignment: .top)
        
        let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(170))
        let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitems: [item])
        
        let decorationItem = NSCollectionLayoutDecorationItem.background(elementKind: DecorationView.identifier)
        
        let section = NSCollectionLayoutSection(group: group)
        section.boundarySupplementaryItems = [header]
        section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10)
        section.decorationItems = [decorationItem]
        
        
        return section
    }
    
    
}

extension CompositionalViewController : UICollectionViewDataSource{
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 2
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        switch section {
        case 0:
            return 10
            
        case 1:
            return 5
            
        default:
            return 0
        }
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        switch indexPath.section {
            
        case 0:
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: FirstSectionCollectionViewCell.identifier, for: indexPath) as! FirstSectionCollectionViewCell
            
            cell.configureCellSubViews()
            
            return cell
            
            
        case 1:
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: SecondSectionCollectionViewCell.identifier, for: indexPath) as! SecondSectionCollectionViewCell
            
            cell.configureCellSubViews()
            
            return cell
            
            
        default:
            return UICollectionViewCell()
        }
        
        
    }
    
    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        
        if kind == UICollectionView.elementKindSectionHeader {
            let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: HeaderView.identifier, for: indexPath) as! HeaderView

            
            switch indexPath.section {
                
            case 0:
                headerView.titleLabel.text = "인기 폭발 프로젝트"
                headerView.decoLabel.text = "Hot"
                
                
            case 1:
                headerView.titleLabel.text = "실시간 프로젝트"
                headerView.decoLabel.text = "New"
                
            default:
                headerView.titleLabel.text = ""
                headerView.decoLabel.text = ""
                
            }
            
            headerView.setupSubViews()
            
            return headerView
        }
        
        return UICollectionReusableView()
    }
    
    
    
}

extension CompositionalViewController : UICollectionViewDelegate {
    
}

 

 

 

Reference:

https://developer.apple.com/documentation/uikit/uicollectionviewcompositionallayout

https://velog.io/@sopt_official/iOS1

https://techblog.gccompany.co.kr/compositional-layout%EA%B3%BC-diffable-datasource%EB%A1%9C-%ED%99%88-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81%ED%95%98%EA%B8%B0-d80ac0d11edd

'UIKit' 카테고리의 다른 글

CGPoint, CGSize, CGRect에 관하여  (0) 2023.01.12
UITableView  (0) 2022.04.12