개발을 잘하고 싶은 주니어?

Compositional Layouts 본문

개발/iOS

Compositional Layouts

데쿠! 2021. 10. 24. 19:30
반응형

요즘 앱들은 여러 개의 섹션에서 따로 collection view로 되어 있고, 좀 더 다양한 레이아웃으로 이루어져 있습니다.

하지만 이것을 UICollectionViewFlowLayout으로 구현하기에는 한계가 있습니다.

그래서 UICollectionViewCompositionalLayout을 이용해서 더 flexible 한 collection view를 구성하도록 합니다.

(iOS 13 or 14 이상의 디바이스에서만 지원됩니다.)

Collection view 구성

compositional layout을 보기 전에 collection view의 레이아웃을 살펴 보겠습니다.

collection view는 여러 개의 items를 반복해서 보여줍니다. 또한 이 item group을 Sections으로 보여줍니다.

여기서 한 section의 item들은 보통 서로 연관이 되어 있습니다.

UICollectionViewCompositionalLayout

iOS13 전에는, UICollectionViewFlowLayout을 사용해서 레이아웃을 구성했습니다.

하지만 이것은 레이아웃을 구성하는데 한계가 있고, 세 개의 columns 나 다른 종류의 collection view를 구성하는 등의 복잡한 레이아웃은 구현하는 것이 불가능했습니다.

그래서 나온 것이 compositional layout입니다.

여기서 애플은 Groups의 개념을 도입했습니다. 적은 양의 코드로 좀 더 정교한 레이아웃을 구성할 수 있습니다.

각각의 노란색 박스가 하나의 그룹

Groups은 Items와 Sections 사이에 있고 이것을 이용해서 section내에 다양한 레이아웃을 구성할 수 있습니다.

func generateLayout() -> UICollectionViewLayout {
  //1
  let itemSize = NSCollectionLayoutSize(
    widthDimension: .fractionalWidth(1.0),
    heightDimension: .fractionalHeight(1.0))
  let fullPhotoItem = NSCollectionLayoutItem(layoutSize: itemSize)
  //2
  let groupSize = NSCollectionLayoutSize(
    widthDimension: .fractionalWidth(1.0),
    heightDimension: .fractionalWidth(2/3))
  let group = NSCollectionLayoutGroup.horizontal(
    layoutSize: groupSize, 
    subitem: fullPhotoItem, 
    count: 1)
  //3
  let section = NSCollectionLayoutSection(group: group)
  let layout = UICollectionViewCompositionalLayout(section: section)
  return layout
}

위는 Compositional Layouts 에서 가져온 collection view의 레이아웃을 만드는 코드입니다.

 

Modern Collection Views with Compositional Layouts

In this tutorial, you’ll learn how to build beautiful, modern UICollectionView layouts using iOS 13’s new declarative UICollectionViewCompositionalLayout API.

www.raywenderlich.com

1. fullPhotoItem은 NSCollectionLayoutItem으로 item의 크기를 파라미터로 가집니다.

여기서 fractional width와 height을 1로 설정하였는데, 이것은 이 아이템을 포함하는 Group에서의 넓이와 높이를 의미합니다.

2. 그러고 나서 NSCollectionLayoutGroup을 만들어서 fractional width를 1로 설정하고 height을 2/3 width로 설정했습니다.

이것은 사진에 대한 aspect ratio를 의미합니다(높이를 넓이의 2/3으로 설정). group은 하나의 horizontal item을 가집니다.

3. 마지막으로 NSCollectionLayoutSection으로 section을 만들어주고 layout의 파라미터로 넘겨줍니다.

아래는 결과 이미지입니다.

 

 

만약 두 개의 column을 가진 레이아웃을 원한다면 아래와 같이 코드를 수정해줍니다.

let groupSize = NSCollectionLayoutSize(
  widthDimension: .fractionalWidth(1.0),
  heightDimension: .fractionalWidth(1/3))
let group = NSCollectionLayoutGroup.horizontal(
  layoutSize: groupSize, 
  subitem: fullPhotoItem, 
  count: 2
)

우선 그룹의 사이즈를 줄이고 count를 2로 설정해서 한 그룹에 두 개의 아이템이 들어가도록 합니다.

만약 사진을 꽉 채우는 것이 아니라 조금의 공간을 남겨두고 싶다고 하면 contentInsets 프로퍼티를 바꿔주면 됩니다.

fullPhotoItem.contentInsets = NSDirectionalEdgeInsets(
  top: 2, 
  leading: 2, 
  bottom: 2, 
  trailing: 2)

 

 

Groups

여기까지는 UICollectionViewFlowLayout에서도 충분히 할 수 있습니다.!

그럼 이제부터는 UICollectionViewCompositionalLayout의 특성을 이용해서 사이즈가 다른 이미지 레이아웃을 만들어보겠습니다.

위의 사진처럼 레이아웃을 구성하기 위해서는 그룹 안에 그룹을 만들면 됩니다. 위의 사진을 나눠보면

1. 제일 위에 큰 사진

2. 두 번째에 왼쪽에 큰 사진 + 수직으로 정렬된 작은 사진 두 개

3. 작은 사진 세 개

4. 2번을 뒤집은 것

 

1. 제일 위의 큰 사진

이건 그냥 위에서 한 방식과 동일하게 레이아웃을 구성하면 됩니다.

// 1
let fullPhotoItem = NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalWidth(2/3)))
    fullPhotoItem.contentInsets = NSDirectionalEdgeInsets(top: 2, leading: 2, bottom: 2, trailing: 2)

2. 두 번째에 왼쪽에 큰 사진 + 수직으로 정렬된 작은 사진 두 개

// 2
let mainItem = NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(2/3), heightDimension: .fractionalHeight(1.0)))
mainItem.contentInsets = NSDirectionalEdgeInsets(top: 2, leading: 2, bottom: 2, trailing: 2)
let pairItem = NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(0.5)))
pairItem.contentInsets = NSDirectionalEdgeInsets(top: 2, leading: 2, bottom: 2, trailing: 2)

let trailingGroup = NSCollectionLayoutGroup.vertical(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1/3), heightDimension: .fractionalHeight(1.0)), subitem: pairItem, count: 2)

let mainWithTrailingGroup = NSCollectionLayoutGroup.horizontal(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalWidth(4/9)), subitems: [mainItem, trailingGroup])

mainItem : 우선 왼쪽의 아이템을 메인 아이템이라고 하고 크기를 지정해줍니다.

pairItem : 그러고 나서 오른쪽에 작은 사진 아이템을 만들고

trailingGroup : 이것을 하나의 그룹으로 묶고(vertical),

mainWithTrailingGroup : 왼쪽의 아이템과 또 하나의 그룹으로 묶습니다(horizontal). 

높이와 넓이는 마음대로 설정해도 되는데 저는 raywenderlich의 수치 그대로 사용했습니다!

3. 작은 사진 세 개

// 3
let tripleItem = NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1/3), heightDimension: .fractionalHeight(1.0)))
tripleItem.contentInsets = NSDirectionalEdgeInsets(top: 2, leading: 2, bottom: 2, trailing: 2)
let tripleGroup = NSCollectionLayoutGroup.horizontal(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalWidth(2/9)), subitem: tripleItem, count: 3)

tripleItem : 세 개의 이미지 중 하나를 의미합니다. 여기서 하나의 그룹에 사진 세 개를 넣을 거니까 넓이는 1/3로 설정하고 높이는 그룹의 높이랑 똑같이 지정할 거니까 1.0을 설정합니다.

tripleGroup : 위의 tripleItem 세 개를 하나의 그룹으로 묶고 (horizontal), 넓이는 꽉 차게 1.0, 높이는 넓이의 2/9로 설정해줍니다.

4. 2번을 뒤집은 것

// 4
let mainWithReversedGroup = NSCollectionLayoutGroup.horizontal(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalWidth(4/9)), subitems: [trailingGroup, mainItem])

뒤집는 건 어렵지 않습니다. 그냥 2번에서 subitems의 순서를 바꿔주기만 하면 됩니다.

5. 위에서 만든 거 수직으로 다 묶기

let nestedGroup = NSCollectionLayoutGroup.vertical(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalWidth(16/9)), subitems: [fullPhotoItem, mainWithTrailingGroup, tripleGroup, mainWithReversedGroup])

let section = NSCollectionLayoutSection(group: nestedGroup)

let layout = UICollectionViewCompositionalLayout(section: section)

nestedGroup : 위의 아이템들을 하나의 그룹으로 묶어줍니다. subitems의 순서는 보이는 순서와 같습니다. 크기를 설정할 때, 높이는 내부 아이템들의 높이값을 고려해서 설정해야 합니다. 만약 내부 아이템들의 높이값보다 작은 값을 설정하면 이미지의 일부가 보이지 않게 됩니다. 또한 더 큰 값을 설정하면 섹션 간의 거리가 넓어지게 됩니다.

section : nestedGroup하나를 하나의 섹션으로 봅니다.

layout : section을 파라미터로 새로운 UICollectionViewCompositionalLayout을 만들어줍니다.

 

 

반응형
Comments