CollectionView를 무작정? 쓰다가 레이아웃 잡을 때 감으로 때려 맞추는 일을 그만하기 위해서 정리를 해보려고 합니다..
Layout
우선 레이아웃을 사용하는 이유는 컬렉션 뷰의 컨텐츠를 정렬하기 위해서입니다.
레이아웃이 잘 잡혀있으면 컨텐츠를 그냥 넣기만 하면 레이아웃대로 구성이 되기 때문이죠!
이 레이아웃을 커스텀 하는 방법으로는 두 가지 방법이 있습니다.
UICollectionViewFlowLayout
UICollectionViewCompositionalLayout
우선 flow 레이아웃은 iOS 6.0+ , compositional 레이아웃은 iOS 13.0 + 에서 사용 가능합니다.
flow 레이아웃이 훨씬 오래됐다는 것을 알 수 있죠
두 레이아웃 객체 모두 UICollectionViewLayout 을 상속받습니다. 이름부터 딱 컬렉션 뷰의 레이아웃이다를 알려주고 있네요.
UICollectionViewFlowLayout
A layout object that organizes items into a grid with optional header and footer views for each section.
각 Section에 대해 헤더와 푸터가 있을 수 있고 item을 그리드로 구성하는 레이아웃 객체입니다.
Flow 레이아웃은 UICollectionViewDelegateFlowLayout 프로토콜을 채택하면 레이아웃을 동적으로 조절할 수 있습니다.
(예를 들어 item의 사이즈를 다양한 크기로 지정할 수 있습니다. 지정하지 않으면 기본값을 사용합니다!)
문서 내용은 아니지만 셀의 크기를 조정하는 방법 두 가지가 더 있다고 합니다.
UICollectionViewFlowLayout 클래스의 itemSize프로퍼티를 이용해 모든 셀을 같은 크기로 설정하는 방법과
셀에 오토 레이아웃을 적용하고 셀 스스로 크기를 결정한 후 이를 UICollectionViewLayout 객체에 알려주는 방법. 이 방법을 사용하려면 estimatedItemSize 프로퍼티를 사용해 대략적인 셀의 최소 크기를 미리 알려줘야 한다고 합니다.
Flow 레이아웃의 기본 스크롤 방향을 세로지만 가로로도 구성할 수 있고,
스크롤 방향의 높이 (or 가로) 는 그리드의 section 및 item 수와 일치되도록 동적으로 조정됩니다.
Flow 레이아웃은 Section 안에 item 을 그리드 방식으로 구현한다는 것을 알 수 있었습니다.!
Flow 레이아웃은 이론적인 부분만 알고 Compositional 레이아웃을 알아보겠습니다.
Compositional Layout 이 나온 이유?
이 Flow 레이아웃에서 애플이 Compositional Layout을 새롭게 낸 이유는 무엇일까요..?
Flow 레이아웃은 복잡한 레이아웃을 구축하는데 한계가 있다고 합니다.
앱스토어만 보더라도 여러 Section 에 모두 다른 구성으로 이루어져 있다는 사실을 알 수 있는데 이를 Flow 레이아웃으로 구현하려면 어렵다고 합니다만.. 사실 Flow 레이아웃을 많이 안 써봐서 확 와닿지는 않네요..ㅎㅎ
아무튼 Compositional Layout을 사용하면 커스텀할 subClass를 생성할 필요 없이 선언적인 접근 방식으로 레이아웃을 단순하게 구성할 수 있다고 합니다.
UICollectionViewCompositionalLayout
A layout object that lets you combine items in highly adaptive and flexible visual arrangements.
높은 적응력과 유연한 시각적인 배열로 item을 결합할 수 있는 레이아웃 객체입니다. (높은 적응력..?)
(아무튼 유연하고 빠르게 설계되었다)
Compositional 레이아웃을 구성하는 방법으로 Flow 레이아웃의 Section 과 item 사이에 Group 의 개념을 도입하여
Section, Group, item 으로 구성할 수 있습니다.
Group 과 Section, item 에는 차이점이 있습니다.
Section 과 item 은 컬렉션 뷰 데이터 소스의 Section, item 과 1:1 로 대응되지만,
Group은 해당하는 데이터 소스가 없습니다.
-> 이 말은 Gruop은 Section 내에서 item의 레이아웃만을 위해 만들어진 개념이라고 볼 수 있습니다.
Compositional Layout 을 코드로 작성해보자
func createBasicListLayout() -> UICollectionViewLayout {
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .absolute(44))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize,
subitems: [item])
let section = NSCollectionLayoutSection(group: group)
let layout = UICollectionViewCompositionalLayout(section: section)
return layout
}
문서에 적혀 있는 가장 기본적인 리스트 레이아웃입니다~
사실 이 코드 때문에 글을 작성하게 됐습니다.
레이아웃을 만드는 단계를 변수명으로만 알아보자면~
1. item 사이즈로 item 레이아웃을 만들고,
2. group 사이즈와 group 안에 들어갈 item으로 group을 만듭니다.
3. 그리고 이 group을 section으로 구성해서 CompositionalLayout을 생성하면 끝!!
그럼 우선 사이즈를 만드는 방법을 알아봅시다.
사이즈는 NSCollectionLayoutSize 라는 객체를 사용해서 만듭니다.
NSCollectionLayoutSize
컬렉션 뷰 item의 width와 height를 나타내는 객체입니다. (item의 범주에는 group도 포함되나 봅니다.)
그리고 생성할 때는 NSCollectionLayoutDimension 타입의 값을 넣어줍니다. (widthDimension, heightDimension)
NSCollectionLayoutDimension 타입의 값을 넣어주는 방법은 세 가지가 있습니다.
1. absolute(CGFloat)
절댓값을 사용하여 44 x 44 정사각형 사이즈를 지정하는 예시입니다.
let absoluteSize = NSCollectionLayoutSize(widthDimension: .absolute(44),
heightDimension: .absolute(44))
2. estimated(CGFloat)
데이터가 로드되거나 글꼴 크기가 변경된 경우 같이 런타임에 콘텐츠 크기가 변경될 수 있는 경우에는
예상 값을 사용합니다. 예상 크기를 제공하면 시스템이 실제 값을 계산하여 사용하게 됩니다.
let estimatedSize = NSCollectionLayoutSize(widthDimension: .estimated(200),
heightDimension: .estimated(100))
3. fractionalWidth(CGFloat), fractionalHeight(CGFloat)
item 의 상대적인 값을 정의할 때는 fractional 값을 사용합니다.
예를 들어 이 코드에서는 item 너비가 20% 의 크기를 가지며 컨테이너의 크기가 변경됨에 따라서 커지고 축소될 수 있습니다.
let fractionalSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.2),
heightDimension: .fractionalWidth(0.2))
이렇게 레이아웃 사이즈 객체를 만들고 나면 NSCollectionLayoutItem, NSCollectionLayoutGroup 을 만들 수 있습니다.
NSCollectionLayoutItem
컬렉션 뷰의 가장 기본적인 구성 요소입니다.
이 객체는 앞서 만든 레이아웃 사이즈 객체로 생성할 수 있습니다.
그리고 edgeSpacing, contentInsets 로 간격을 조정할 수 있습니다!
NSCollectionLayoutGroup
경로 (스크롤등,,) 에 따라 item을 배치하는 컨테이너입니다.
생성 시 horizontal, vertical 을 설정할 수 있으며 custom 도 가능합니다. (어떻게 custom 하는지는 나중에..?)
그리고 subitems에 만들었던 NSCollectionLayoutItem 타입의 item을 넣어줍니다.
group에서도 interItemSpacing 값을 통해 item 간격을 조정할 수 있습니다.
NSCollectionLayoutSection
group 들을 이제 결합하는 컨테이너입니다.
앞서 만든 Group으로 Section을 만들어주면 이제 CompositionalLayout을 생성할 수 있습니다.
UICollectionViewCompositionalLayout
let layout = UICollectionViewCompositionalLayout(section: section)
Flow layout 과의 가장 큰 차이점은 group이 포함되었다는 점인 것 같습니다. ㅎ.ㅎ 그리고 레이아웃 구성 방법!!
어떤 게 더 좋다고 정답은 없지만 코드를 보고 이해하기 쉬운 것은 Compositional Layout이라고 생각합니다.
+ 번외로 CompositionalLayout 에서 list 를 생성할 수도 있습니다. (iOS 14.0+)
개인적인 사용 경험으로는 기본적인 List 를 구성하는 데는 매우 편리하지만
커스텀의 영역으로 넘어가면 구성하기 어렵다는 느낌을 많이 받았습니다 ㅠ.ㅠ
더 많은 리스트 구현이 있으면 좋겠다는 개인적인 바람입니다 ㅎ.ㅎ
아래 코드를 다운로드하면 다양한 Compositional Layout 예시를 확인해 볼 수 있습니다. ( + Diffable DataSource )
https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/layouts
https://developer.apple.com/documentation/uikit/uicollectionviewcompositionallayout
https://lickability.com/blog/getting-started-with-uicollectionviewcompositionallayout/
https://developer.apple.com/documentation/uikit/nscollectionlayoutsize
https://developer.apple.com/documentation/uikit/nscollectionlayoutitem
'iOS > STUDY' 카테고리의 다른 글
[iOS] UITabBarController, UITabBar (0) | 2022.09.01 |
---|---|
[iOS] Static, Dynamic Library, Framework (0) | 2022.06.20 |
[iOS] 카카오 로그인 이해하기 (0) | 2022.05.18 |
[iOS] UIToolbar 이슈 (UIToolbarContentView, UIButtonBarStackView) (0) | 2022.04.29 |
[iOS] 레이아웃이 깨졌을 때 콘솔창 버그 (0) | 2022.04.26 |