본문 바로가기

iOS/앱센터 스터디

UICollectionViewCompositionalLayout에 대해서 알아보자

 

 

이번에 앱센터에서 Netflix 스타일의 앱을 프로젝트로 진행했다.

 

해당 화면같이 다양한 구조의 화면 배열을 구현하기 위해서 기존의 CollectionView의 UICollectionViewFlowLayout객체로만으로는 구현하기가 불가능하다.

따로 ScrollView와 ContainerView, collectionView등 다양한 View들을 사용해야 구현이 가능하지만

UICollectionViewFlowLayout을 통해 해당 화면과 비슷한 구성을 CollectionView만으로 구현할 수 있다.

 


UICollectionViewCompositionalLayout이란?

  • 별개의 구조로 나뉘는 섹션들마다의 레이아웃을 지정해준다.
  • Section, Group, item으로 구성되어있으며 의도대로 배치할 수 있다.

Section(NSCollectionLayoutSection)

  • Collection View는 하나 이상의 섹션이 있다.
  • 각 섹션은 다른 섹션과 구분하기 위해 background, header, footer을 가질 수 있다.
  • 섹션의 레이아웃은 NSCollectionLayoutGroup의 속성에 따라 결정된다.
  • NSCollectionLayoutSection객체를 지정된 그룹을 포함하는 섹션을 만든다.

 


Group (NSCollectionLayoutGroup)

 

  • Group은 Collection View에서 item들이 배치되는 방식을 결정한다.
  • item들을 가로, 세로 또는 사용자 지정 배열로 배치할 수 있다.
  • 각 그룹은 NSCollectionLayoutDimension을 이용해 width, heigth를 지정한다.
  • Group을 구성한 뒤에는 Section의 NSCollectionLayoutSectionf로 Section을 초기화해줘야 한다.

 


Item (NSCollectionLayoutItem)

 

 

  • Item은 Collection View에서 개별 콘텐츠의 크기, 공간 및 정렬을 하는 방법에 대한 blueprint이다.
  • 일반적으로 Item은 Cell이지만 header, footers, decorations와 같은 Supplementary View가 될 수 있다.
  • 예를 들어 Item은 하나의 사진만 가지고 있는 Cell로 표현할 수 도 있고, 사진의 이름, 버튼과 같은 정보를 표시하는 Cell로도 표현할 수 있다.
  • 각 item은 NSCollectionLayoutDimension을 이용해 width와 heigth를 지정한다.

 

 


NSCollectionLayoutSection 구현

UICollectionViewCompositionalLayout은 초기값으로 NSCollectionLayoutSection이 필요하다.

따라서 먼저 NSCollectionLayoutSection을 구현하도록 하자.

 

    // 첫번쨰 cell의 NSCollectionLayoutSection을 반환해주는 함수
    func homeFirstCreateCompositionalLayout() -> NSCollectionLayoutSection {
        
        // 아이템들의 layoutSize인 width, heigth 설정
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(466))
        // 아이템의 layoutSize를 가진 NSCollectionLayoutItem 객체 생성 -> item 생성
        let item = NSCollectionLayoutItem(layoutSize: itemSize)
        
        // 그룹의 layoutSize인 width, heigth 설정
        let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(480))
        // 그룹의 layoutSize와, subitem -> 그룹안에 들어갈 item, count -> 그룹당 보여질 아이템 수를 가진 NSCollectionLayoutGroup 객체 생성 -> group 생성
        let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitem: item, count: 1)
        
        // 섹션의 group을 가진 NSCollectionLayoutSection객체 생성 -> section 생성
        let section = NSCollectionLayoutSection(group: group)
        // 섹션의 마진 값 설정
        section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 20, trailing: 0)
        
        return section
    }

해당 코드는 NSCollectionLayoutSection을 반환해주는 함수이다.

코드를 차례대로 보면 item 객체를 만들고 그 item 객체를 이용해 group객체를 만들고, 그 group객체를 통해 section 객체를 만들고 있다.

차례대로 item → group → section순으로 만들어주면 된다.

 

// 나머지 cell의 NSCollectionLayoutSection을 반환해주는 함수
    func homeSecondCreateCompositionalLayout() -> NSCollectionLayoutSection {

        let itemSize = NSCollectionLayoutSize(widthDimension: .absolute(120.0), heightDimension: .absolute(160.0))
        let item = NSCollectionLayoutItem(layoutSize: itemSize)
        item.contentInsets = NSDirectionalEdgeInsets(top: 1, leading: 1.5, bottom: 1, trailing: 1.5)

        let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(160.0))
        let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 3)
        
	// 헤더의 layoutSize인 width, height 설정
        let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(30))
	// NSCollectionLayoutBoundarySupplementaryItem 객체를 이용해 헤더 생성
        let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: UICollectionView.elementKindSectionHeader, alignment: .topLeading)
        
        let section = NSCollectionLayoutSection(group: group)
	// 섹션에 헤더를 추가
        section.boundarySupplementaryItems = [ sectionHeader ]
        section.orthogonalScrollingBehavior = .continuous
        
        return section
    }
}

첫번째 함수와 다르게 이번에는 header를 추가해줬다.

NSCollectionLayoutBoundarySupplementaryItem객체는 인자로 layoutSize, elementKind, alignment가 있는데 순서대로

layoutSize → 헤더의 layoutSize

elementKind → header인지 footer인지 구분

alignmet → 정렬위치이다.

 


UICollectionViewCompositionalLayout 구현

lazy var collectionView: UICollectionView = {
    
        // sectionIndex를 인자로 받아 NSCollectionLayoutSection타입을 반환하는 클로저
        let layout = UICollectionViewCompositionalLayout { (sectionIndex, _) -> NSCollectionLayoutSection? in
            switch sectionIndex {
            case 0:
                return self.homeFirstCreateCompositionalLayout()
            default:
                return self.homeSecondCreateCompositionalLayout()
            }
        }
        
        // 위에 선언한 UICollectionViewCompositionalLayout을 collectionView의 layout으로 넣어줌
        let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
        
        // collectionView에 cell과 view를 등록해줌
        cv.register(HomeTopCell.self, forCellWithReuseIdentifier: HomeTopCell.identifier)
        cv.register(HomeCell.self, forCellWithReuseIdentifier: HomeCell.identifier)
        
        // HomeHeaderView는 kind 값을 elementKindSectionHeader로 넣어줌 -> 헤더로 사용하기 위해
        cv.register(HomeHeaderView.self, forSupplementaryViewOfKind:
                                    UICollectionView.elementKindSectionHeader, withReuseIdentifier: HomeHeaderView.identifier)
        
        return cv
    }()

섹션마다 layout을 다르게 하기 위해 UICollectionViewCompositionalLayout을 sectionIndex를 인자로 받아 NSCollectionLayoutSection을 반환하는 클로저로 설정해줬다.

 

이렇게 되면 맨 처음 스크린샷과 같이 다양한 레이아웃을 가지는 1개 이상의 섹션들을 가지는 Collection View를 만들 수 있는 것이다.

 

요즘 대부분의 앱들은 앱 구성이 다 재각각이기 때문에 해당 UICollectionViewCompositionalLayout는 앞으로 앱 개발에 있어서 필수적일 거 같다…..

 

해당 코드는  여기서 확인하실 수 있습니다!

 

GitHub - leemhyungyu/NetflixExample_AppCenter

Contribute to leemhyungyu/NetflixExample_AppCenter development by creating an account on GitHub.

github.com

 

'iOS > 앱센터 스터디' 카테고리의 다른 글

앱센터 스터디 1주차 정리  (1) 2022.04.10