[iOS] Advances in UICollectionVIew

UICollectionView

iOS6 때 처음으로 UICollectionView가 출시되었다. 이때부터, UICollectionView를 구성하는 API는 Data, Layout, Presentation 세 가지 카테고리로 구분되었다.

  • Data: 어떤 데이터를
  • Layout: 어떤 레이아웃에
  • Presentation: 어떻게 렌더링하여 보여줄 것인가

이러한 구분이 UICollectionView가 유연하게 동작할 수 있게 하는 핵심이다.

iOS6부터 data와 layout을 나타내는 방법은 바뀌었을지언정 카테고리 구분은 변하지 않았다.

 

iOS 13 이전

iOS6 ~ iOS 12까지, 즉 iOS 13 이전의 CollectionView를 구성하는 것은 아래와 같다.

etc-image-0

기존에는 셀의 위치를 indexPath를 기준으로 CollectionView Cell을 그리고, 정렬했다.

DataindexPath-based protocolUICollectionViewDataSource로 관리되었다. 해당 프로토콜에는 collectionView(_:numberOfItemsInSection:) 과 collectionView(_:cellForItemAt:) method가 필수적으로 구현되어야 했다.

@MainActor public protocol UICollectionViewDataSource : NSObjectProtocol {

    
    @available(iOS 6.0, *)
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int

    
    // The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:
    @available(iOS 6.0, *)
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
    
    ... ...

 

Layout은 추상 클래스인 UICollectionViewLayout과 구상 클래스인 UICollectionViewFlowLayout을 통해서 잡아줄 수 있었다.

    static func setCollectionViewLayout() -> UICollectionViewFlowLayout {
        let layout = UICollectionViewFlowLayout()
        let space: CGFloat = 12
        
        let width = UIScreen.main.bounds.width - (space * 3)
        layout.itemSize = CGSize(width: width / 2, height: width / 2 * 1.45)
        
        layout.sectionInset = UIEdgeInsets(top: space, left: space, bottom: 4, right: space)
        
        layout.minimumLineSpacing = space
        layout.minimumInteritemSpacing = space
        layout.scrollDirection = .vertical
        
        return layout
    }

 

Presentation은 UICollectionViewCell 및 UICollectionViewReusableView라는 View 타입이 제공되었다.

 

근데 왜 iOS 13 때 CollectionView가 대대적으로 변경되었을까?

그 이유를 생각해 보면, 일단 iOS 13 이전의 CollectionView를 구성하는 방식이 Index를 기반으로 하기 때문인 것이 가장 큰 이유일 것이다.

IndexPath를 기반으로 구성하게 되면 무엇이 좋지 않을까?

  • indexPath를 기반으로 데이터와 레이아웃을 구성하게 될 경우 기존의 데이터가 아닌 인덱스를 기준으로 구성하게 된다.
  • 런타임 이슈의 단골 손님인 `out of range` 또한 indexPath를 기반으로 구성해서 일어나게 된다.

또, 기존의 다른 단점이 있었다면...

  • Animation 기능이 없었다!
    • 개인적으로 애니메이션을 추가하게 되면 오류가 생기는 경우가 잦았다.
  • 매번 데이터를 새로 가져올 때, reloadData를 해 줬어야 했다.

 

 

iOS 13

그래서~~~!! iOS 13 때 대대적으로 CollectionView가 바뀌게 된다.

 

etc-image-1

 

가장 큰 변화는 UICollectionView를 구성하는 API의 변화이다.

 

Data DiffableDataSource를 이용하여 관리할 수 있다.

Snapshots을 추가하여 UI 상태 관리를 간소화할 수 있게 되었다. Snapshot은 고유한 섹션 및 itemIdentifier를 이용하여 전체 상태 UI를 캡쳐하듯이 저장해 놓았다가 UI가 달라지게 되면 그 차이점을 토대로 애니메이션을 구현해 준다. 

근데 왜 캡쳐하면서까지 저장할까? 단지 애니메이션 때문일까?! => XXX

애니메이션을 적용하기 위해서... 는 맞지만 본질적으로는 애니메이션 때문이라기보다는 UI가 고유한 상태를 유지하기 위해서 그렇다!

어라? 이전에는 중요하지 않던 `고유성`이 이제는 중요해지게 된다.

고유성 하면 생각나는 프로토콜이 하나 있지 않은가?

 

바로 해셔블~~~!!!! 이 여기서 나오게 댄다. 

추후에 보겠지만, DiffableDataSource의 경우에는 dataSource를

var dataSource: UICollectionViewDiffableDataSource<section, cell>

이런 식으로 선언해 주어야 하는데, 이때 section과 cell에 들어가는 타입들이 모두 hashable해야 한다.

만약에 hashable하지 않은 타입을 그냥 넣게 된다면

etc-image-2

요런 오류를 뱉게 된다.

 

아무튼 하라는대로 구현하게 되면 개발자의 추가 작업 없이 차이점을 계산하고 자동으로 애니메이션을 생성해 주게 된다!

 

 

LayoutCompositional Layout를 통해서 잡아주게 된다.

기존의 레이아웃과 달리 compositionalLayout은 layout의 작동 방식이 아닌 단순한 모양을 묘사하는 것이므로 훨씬 빠르고, section마다 구분되는 레이아웃들을 사용하여 더욱 정교한 UI를 만들 수 있다.

또한!!!!!! 기존에는 스크롤뷰 형식으로 구현해야 했던 가로 scrolling section도 지원한다.

Presentation의 경우에는 기존과 동일하다!

하지만, 가장 큰 변화가 있었다. 기존에 index를 기준으로 collectionView를 구성하는 것이 아니라 Data를 기반으로 CollectionView를 구성하게 되었다.

 

 

 

또, Index를 기반으로 하는 것과는 별개로...

tableView와 비교했을 때, tableView는 systemCell이 있는데, CollectionView는 없었다.

간단한 UI를 만들게 되더라도 레이아웃을 매번 잡아주어야 했던 것이다.

따라서, Apple은 자기들이,,, tableView에서의 systemCell과 같은 부분을 CollectionView에서도 제공해 줄 수 있지 않을까? 하게 된다.

 

 

 

 

 

 

iOS 14

iOS 14에서는 iOS 13을 기반으로 새로운 기능들을 추가하게 되었다.

먼저 살펴 보자면, Diffable Data Source를 바탕으로 Section Snapshots을,

Compoistional Layout을 바탕으로 tableView의 systemCell과 같은 List Configuration을!

기존의 UICollectionViewCell / UICollectionViewReusableView로 구성되어 있던 렌더링 방식에는 ListCell과 ViewConfiguration으로 바뀌게 되었다.

 

etc-image-3

 

Data의 경우에는 section snapshots이라는 새로운 타입이 생기게 된다!

이름에서 보이듯이 UICollectionView의 내부 데이터를 캡쳐해서 저장했던 것처럼 section의 데이터까지 캡쳐하여 사용하게 될 수 있게 되었다.

 

이렇게 변화한 이유에는 두 가지가 있는데,

1. section 단위로 data를 쉽게 구성하기 위해서

2. outlet 스타일의 UIRendering을 지원하는데 필요한 계층적 데이터의 모델링을 허용하기 위해서이다.

 

엥? 1번은 그렇다 치지만 2번은 도통 뭔소린지 모르겠다

 

흠. 예시를 보자!

etc-image-4etc-image-5
왼: iOS 13 / 오: iOS 14

애플이 제공하는 Modern 어쩌고 CollectionVIew 프로젝트에 있는 이모지 예시이다.

사진에서 알 수 있다시피 horizentally scrolling section은 single section snapshot으로 구현되어 해당 부분에서 사용되는 content를 모델링하고 있다.

또, expadable-collasible outline style로 구현된 두 번째 섹션은 또 하나의 single section snapshot으로 구성되어 데이터를 모델링한다.

세 번째 list section도 역시 single section snapshot을 통해서 모델링 할 수 있게 되었다.

그러니까, 더욱 효율적으로 코드를 구성할 수 있게 된 것이다! 

 

 

 

Layout의 경우에는 iOS 13에서의 compositional Layout을 기반으로 만든 새로운 기능을 가지고 있는 List Configuration이 나왔다.

Lists는 모든 UICollectionView에 UITableView와 유사한 UI로 구성할 수 있게 되었다. 또한, TableView에서 기대할 수 있는 다양한 기능들(swipe, common cell layouts)을 사용할 수 있게 되었다!

또한, 섹션별로 다른 레이아웃과 lists를 쉽게 혼합하고 매칭할 수 있게 되었다.

let configuration = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
let layout = UICollectionViewCompositionLayout.list(using: configuration)

list Layout의 경우에는 위와 같이 잡을 수 있으며, 기존의 tableView의 systemCell을 이용하듯이 기존에 있는 ImageView에 사진을 넣거나, label에 특정 텍스트를 추가하거나 할 수 있다.

 

 

Presentation의 경우에는 Cell RegistrationsConfiguration을 통해 셀을 새로운 방식으로 구성할 수 있게 되었다.

cell Registration은 view Model로부터 cell을 설정하는 방법으로, 기존에 reuse Identifier를 사용하여 cell classsk nib을 등록하는 과정을 생략하고 데이터를 이용하여 configuration closure를 사용하는 registration type을 사용하게 된다.

    var cellRegistration: UICollectionView.CellRegistration<UICollectionViewListCell, TodoTable>!
    
    ... ...
    
            // itemIdentifier = list[indexPath.item]
        // cell for item at 에서 data로 연결해 줬기 땜에 itemIdentifier에 들어가는 거임!!
        cellRegistration = UICollectionView.CellRegistration(handler: { cell, indexPath, itemIdentifier in
            
            var content = UIListContentConfiguration.valueCell()
            content.image = itemIdentifier.favorite ? UIImage(systemName: "star.fill") : UIImage(systemName: "star")
            content.text = itemIdentifier.title
            content.secondaryText = "\(itemIdentifier.detail.count)개의 세부 할 일"
            cell.contentConfiguration = content
            
        })

이런 식으로!

셀을 등록하면서 동시에 데이터에 따라서 구성해 줄 수 있다.

 

 

 

Configuration의 경우에는 앞서 말했듯 표준화된 레이아웃을 제공하는 친구라서 그 종류만 간단히 살펴보고 넘어가도록 하겠다.

 

etc-image-6
UIListContentConfiguration.cell()

 

etc-image-7
UIListContentConfiguration.valueCell()

 

etc-image-8
UIListContentConfiguration.subtitleCell()

 

이렇게!

 

 

 

추후에 코드 구성법이나 관련된 부분은 따로 올려 보도록 하겠다.

나중에 링크 추가하겠음~~~~!! ^___^

 

 

 

 

 


 

 

 

진짜 첨 배웠을 때부터 정리하고 싶었는데

정리를 .. 못햇다

너무이해가안대서 

ㅋㅋ 하 .......

기존의 tableView랑 collectionView 방식으로는 걍 쉽게쉽게 구현하겟는데 이건 왜케어려운건지

그래도 이제는 좀 코드를 쳐보니까 이해가가는거같다...

사실근데아직도아닌거같기도하고

내생각엔 아직 코드 치는 양이 절대적으로!!!!! 부족해서 그런 거 같다

손코딩도 하고 새로 뷰도 짜보고 하면서 연습해봐야겠다

 

 

 

 

참고 자료

CollectionViews: https://developer.apple.com/documentation/uikit/views_and_controls/collection_views

 

Collection views | Apple Developer Documentation

Display nested views using a configurable and highly customizable layout.

developer.apple.com

 

Implementing Modern Collection Views: https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/implementing_modern_collection_views

 

Implementing Modern Collection Views | Apple Developer Documentation

Bring compositional layouts to your app and simplify updating your user interface with diffable data sources.

developer.apple.com

 

Updating Collection Views Using Diffable Data Sources: https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/updating_collection_views_using_diffable_data_sources

 

Updating Collection Views Using Diffable Data Sources | Apple Developer Documentation

Streamline the display and update of data in a collection view using a diffable data source that contains identifiers.

developer.apple.com

 

 

Advances in UICollectionView: https://developer.apple.com/videos/play/wwdc2020/10097/?time=141

 

Advances in UICollectionView - WWDC20 - Videos - Apple Developer

Learn about new features of UICollectionView that make it easier to use and unlock powerful new functionality. We'll show you how to use...

developer.apple.com

 

Advances in UI Data Sources: https://developer.apple.com/videos/play/wwdc2019/220

 

Advances in UI Data Sources - WWDC19 - Videos - Apple Developer

Use UI Data Sources to simplify updating your table view and collection view items using automatic diffing. High fidelity, quality...

developer.apple.com

 

Advances in diffable data sources: https://developer.apple.com/videos/play/wwdc2020/10045

 

Advances in diffable data sources - WWDC20 - Videos - Apple Developer

Diffable data sources dramatically simplify the work involved in managing and updating collection and table views to create dynamic and...

developer.apple.com

 

Advances in Collection View Layout: https://developer.apple.com/videos/play/wwdc2019/215

 

Advances in Collection View Layout - WWDC19 - Videos - Apple Developer

Collection View Layouts make it easy to build rich interactive collections. Learn how to make dynamic and responsive layouts that range...

developer.apple.com

 

Lists in UICollectionView: https://developer.apple.com/videos/play/wwdc2020/10026

 

Lists in UICollectionView - WWDC20 - Videos - Apple Developer

Learn how to build lists and sidebars in your app with UICollectionView. Replace table view appearance while taking advantage of the full...

developer.apple.com

 

Modern cell configuration: https://developer.apple.com/videos/play/wwdc2020/10027/

 

Modern cell configuration - WWDC20 - Videos - Apple Developer

Discover new techniques for configuring collection view and table view cells to quickly build dynamic interfaces in your app. Explore...

developer.apple.com

 

'iOS' 카테고리의 다른 글

[Swift] 프로젝트명 변경하기  (6) 2023.11.11
[Swift] 정규표현식  (2) 2023.11.10
[Swift] ARC(Automatic Reference Counting) (2/2)  (2) 2023.09.02
[Swift] Singleton Pattern은 왜 class로만 만들까?  (0) 2023.08.13
[SeSAC] August 9, 2023  (0) 2023.08.09