Compositional Layout

Dophi·2023년 12월 28일
0

iOS

목록 보기
5/5

소개글

오랜만에 UIKit을 다시 써보면서 새로운 개념들도 알아가고 있고 소마에서 배웠던 기술들도 적용해보고 있습니다. 이러한 것들에 대해 개념을 자세히 설명하기보다는 실제로 어떻게 썼는지, 어떤 점이 좋았는지 정리해보고자 합니다!

Compositional Layout

Compositional Layout에 대해 소개하기 앞서, 아래와 같이 가로 스크롤, 세로 스크롤이 공존하는 화면을 구현하려면 어떻게 해야할까요?

보통 많이 알려져있는 방법 중 하나는 아래와 같습니다.

  1. 먼저 TableView를 구현
  2. TableView 중 하나의 셀을 CollectionView로 구현
  3. CollectionView를 구현할 때는 Flow Layout 객체에서 scrollDirection = .horizontal 로 설정

하지만 이렇게 하면 TableView와 CollectionView를 모두 정의해야하다보니 상당히 코드가 복잡해져버립니다.

그래서 이런 화면을 구현하기 위해 사용할 수 있는 또 다른 방법은 Compositional Layout을 사용하는 것입니다.

  1. CollectionView만 구현
  2. 특정 섹션에 orthogonalScrollingBehavior 속성만 설정

scrollDirectionorthogonalScrollingBehavior 모두 가로 스크롤을 가능하게 해주는 속성입니다. 하지만 차이점이 있는데 scrollDirection 은 컬렉션뷰 자체에, 즉 모든 섹션에 전부 적용되버리고, orthogonalScrollingBehavior은 특정 섹션에만 적용할 수 있다는 것입니다. 그러다보니 TableView, CollectionView 여러개를 복잡하게 정의할 필요가 없어지는 거죠.


그림 출처

Compositional Layout에 대해 좀 더 설명하자면 Item, Group, Section 이라는 개념이 존재합니다. 간단히 말하자면 Section 안에 Group이 있고, Group 안에 Item이 있는 형태입니다.

그리고 각각의 요소마다 사이즈를 정할 때 세가지 표현을 사용할 수 있습니다.

  • absolute: 고정 크기
  • fractional: 현재 속한 컨테이너에서 차지하는 비율
  • estimated: 크기가 변경될 가능성이 있는 경우 예상 크기

코드

코드로 어떻게 구현했는지 설명드리겠습니다.

아래 코드는 제가 실제로 프로젝트를 하면서 썼던 Compositional Layout 코드입니다. 프로젝트 전체 보기

private lazy var collectionViewLayout = UICollectionViewCompositionalLayout { (section, env) -> NSCollectionLayoutSection? in
	switch self.viewModel.sections[section] {
    case .TopFiveProducts:
		// item
		let itemSize = NSCollectionLayoutSize(
			widthDimension: .fractionalWidth(1.0),
			heightDimension: .fractionalHeight(1.0)
		)
		let item = NSCollectionLayoutItem(layoutSize: itemSize)
		
		// Group
		let groupSize = NSCollectionLayoutSize(
			widthDimension: .fractionalWidth(1.0),
			heightDimension: .absolute(200)
		)
		let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
		
		// Section
		let section = NSCollectionLayoutSection(group: group)
		section.orthogonalScrollingBehavior = .groupPaging
		section.contentInsets = .init(top: 60, leading: 0, bottom: 30, trailing: 0)
		section.boundarySupplementaryItems = [
			NSCollectionLayoutBoundarySupplementaryItem(
				layoutSize: .init(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(30)),
				elementKind: UICollectionView.elementKindSectionHeader,
				alignment: .top
			)
		]
		return section
        
	case .AllProducts:
		// item
		let itemSize = NSCollectionLayoutSize(
			widthDimension: .fractionalWidth(1.0),
			heightDimension: .fractionalHeight(1.0)
		)
		let item = NSCollectionLayoutItem(layoutSize: itemSize)
		
		// Group
		let groupSize = NSCollectionLayoutSize(
			widthDimension: .fractionalWidth(1.0),
			heightDimension: .fractionalHeight(1.0 / 4.0)
		)
		let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitems: [item])
		group.contentInsets = .init(top: 0, leading: 10, bottom: 0, trailing: 0)
		
		// Section
		let section = NSCollectionLayoutSection(group: group)
		section.contentInsets = .init(top: 60, leading: 0, bottom: 30, trailing: 0)
		section.boundarySupplementaryItems = [
			NSCollectionLayoutBoundarySupplementaryItem(
				layoutSize: .init(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(30)),
				elementKind: UICollectionView.elementKindSectionHeader,
				alignment: .top
			)
		]
		return section
	}
}

Compositional Layout을 쓴 덕분에 switch문을 통해 섹션별로 나누고, 각각 item, group, section을 정의할 수 있었습니다. .fractional.absolute를 사용하여 각각의 사이즈를 정의했습니다.

Group과 Section에 패딩 값을 주기 위해 contentInset도 정의했습니다.

추가적으로 CollectionView Header도 정의를 해서 사용하고 있었는데, 헤더의 레이아웃도 조정하기 위해 boundarySupplementaryItems도 정의했습니다.

이렇게 해서 완성된 화면은 아래와 같습니다.

마무리

이상으로 Compositional Layout에 대해 알아봤습니다. 저도 현재 배우고 있는 입장이기 때문에 틀린 개념, 부족한 개념들이 있을 수 있습니다. 혹시라도 그런 부분이 있다면 언제든지 지적해주셔도 됩니다. 긴 글 읽어주셔서 감사합니다. 😊

profile
개발을 하며 경험한 것들을 이것저것 작성해보고 있습니다!

0개의 댓글

관련 채용 정보