감자주먹밥

[IOS] CollectionView Diffable DataSource 적용 본문

IOS/UIKit

[IOS] CollectionView Diffable DataSource 적용

JustHm 2023. 5. 28. 22:03
728x90

Diffable Data Source?


Diffable DataSource는 WWDC 19에 발표되었고, IOS 13 이상 부터 사용가능하다.

TableView, CollectionView의 DataSource에서 개선된 것임으로 둘 다 적용이 가능하다.

변경된 Data를 적용해야 할 때 그냥 reloadData를 써버리거나, performBatchUpdates로 애니메이션 까지 신경쓰며 갱신을 해 줬지만, 

Diffable DataSource를 사용하면 첫 설정에 UI에 대한 설정 말고는 데이터가 변할 때 마다 apply만 해 주면 알아서 애니메이션 까지 자연스럽게 적용할 수 있다.

적용하기


먼저 결과 화면

먼저 스토리 보드를 사용해 collectionView를 추가했다.

Diffable DataSource를 사용하기 위해선 3가지를 설정해야 한다.

1. 사용할 CollectionView의 Layout 설정

2. 사용할 CollectionViewCell Registration

3. UICollectionViewDiffableDataSource 생성, 할당

CollectionView의 Layout 설정

collectionView.collectionViewLayout = createListLayout()

private func createListLayout() -> UICollectionViewCompositionalLayout {
    var config = UICollectionLayoutListConfiguration(appearance: .plain)
    var separator = UIListSeparatorConfiguration(listAppearance: .plain)
    
    separator.color = .white
    config.backgroundColor = .clear
    config.separatorConfiguration = separator
    config.trailingSwipeActionsConfigurationProvider = makeSwipeActions
    
    return UICollectionViewCompositionalLayout.list(using: config)
}

커스텀한 Layout을 만드는 방법은 저번에 item, group, section 별로 만들었던 글이 있어서 이번에는 ListCell을 사용해 봤다.

ListCell을 사용할 땐 UICollectionLayoutListConfiguration을 만들어 Cell의 UI를 설정할 수 있다.

Cell에 있는 separator의 색도 바꾸고 싶어 UIListSeparatorConfiguration을 사용해 색을 변경하고 UICollectionLayoutListConfiguration에 주입시켜 줬다.

이 외에도 header, footer를 설정할 수 있어 공식문서에서 더 찾아보면 정보를 얻을 수 있다.

 

UICollectionLayoutListConfiguration | Apple Developer Documentation

A configuration for creating a list layout.

developer.apple.com

config.trailingSwipeActionsConfigurationProvider 프로퍼티가 있는데 이건 셀을 오른쪽에서 왼쪽으로 쓸어 넘겼을 때 액션을 추가할 수 있다. trailling, leading 둘 다 존재하고 UISwipeActionsConfiguration를 구성해서 넘겨주면 적용이 가능하다.

위 코드에선 함수로 따로 작성해서 주입했다.

private func makeSwipeActions(for indexPath: IndexPath?) -> UISwipeActionsConfiguration? {
    guard let indexPath = indexPath,
          let item = dataSource.itemIdentifier(for: indexPath) else { return nil }
    
    let deleteAction = UIContextualAction(style: .destructive, title: "Delete") { [weak self] _, _, completion in
        self?.input.send(.deleteArticle(article: item))
    }
    return UISwipeActionsConfiguration(actions: [deleteAction])
}

Swipe Action이 생기면 이 함수로 indexPath가 넘어오고 설정한 Action이 해당 Cell에 동작을 하게 된다.

단순히 해당 셀의 데이터 삭제만 하기 때문에 handler의 파라미터로 넘어오는 UIContextualAction, UIView, @escaping (Bool) -> Void는 따로 사용하지 않았다.

CollectionViewCell Registration

typealias CellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, Article>

let cellRegisteration = CellRegistration { cell, indexPath, itemIdentifier in
    var configuration = cell.defaultContentConfiguration()
    var background = UIBackgroundConfiguration.listPlainCell()
    
    configuration.image = UIImage(systemName: itemIdentifier.weather)?.withRenderingMode(.alwaysOriginal)
    configuration.text = "Record at \(itemIdentifier.date)"
    configuration.textProperties.color = .white
    
    background.backgroundColor = .clear
    
    cell.accessories = [.disclosureIndicator()]
    cell.backgroundConfiguration = background
    cell.contentConfiguration = configuration
}

Layout 설정이 끝나면 두 번째로 Cell을 Regist 해야 한다.

Diffable DataSource를 사용할 땐 CellRegistration 타입을 사용해야만 설정이 가능하다.

먼저 CellRegistration에 사용할 Cell, 사용할 Data를 설정하고 생성하면 된다.

여기서 쓴 생성자는 그냥 Handler만 있는 생성자를 사용했다. 따로 Custom 한 Cell을 만들어 사용하지 않고 ListCell을 이용했기 때문.

만약 내가 Cell UI를 직접 디자인해서 사용한다면 아래의 생성자를 사용해서 Regist 하면 된다.

Handler의 Parameter로는 Cell, IndexPath, ItemIdentifier가 들어오는데 itemIdentifier는 display 될 Data라고 보면 된다.

나머지 코드는 기존에 DequeueReusableCell을 하면 하던 것들.. 근데 cell의 accessories를 추가하는 건 이번이 처음이었다.

하나만 사용하면 됐기에 간단하게 추가만 하고 끝났는데, 모든 Accessory에 대해 잘 정리해 주신 글이 있어 추가적인 정보를 얻기 좋았다.

 

[iOS] TableViewCell에 Accessory 표시하기

부스트코스 iOS 3차 프로젝트 ‘WeatherToday’ 과제는 다음과 같은 요구사항이 있다.

sujinnaljin.medium.com

UICollectionViewDiffableDataSource 생성, 할당

CellRegistration을 생성하고 나면 이제 마지막으로 DiffableDataSource를 생성하면 된다.

var dataSource: UICollectionViewDiffableDataSource<Section, Article>!

dataSource = UICollectionViewDiffableDataSource<Section, Article>(collectionView: collectionView,
cellProvider: { collectionView, indexPath, itemIdentifier in
    collectionView.dequeueConfiguredReusableCell(using: cellRegisteration, for: indexPath, item:
itemIdentifier)
})

CollectionView가 있는 VC에 먼저 dataSource를 만들어 놓았다. 신기하게도 이렇게 해서 생성 후 주입만 하면 알아서 collectionView가 체크하고 적용이 된다.

DiffableDataSource에는 사용할 Section, Data가 타입에 들어가는데, Section의 경우 하나일지라도 생략을 할 순 없다.

생성자 파라미터로는 사용할 CollectionView를 넣어주고, cellProvider의 Closure를 설정하면 된다.

여기서 마지막으로 CollectionView의 Cell을 직접 할당해 주는 클로저를 작성하여 dataSource에 주입시키는 것이다.

마지막, Data apply

var snapshot: NSDiffableDataSourceSnapshot<Section, Article>!

snapshot = NSDiffableDataSourceSnapshot<Section, Article>()
snapshot.appendSections([.main])
snapshot.appendItems(data, toSection: .main)
dataSource.apply(snapshot)

Cell의 data를 주입시켜 표시하려면 SnapShot을 사용해 데이터를 먼저 주입시켜야 한다.

SnapShot은 DataSource와 같은 타입으로 사용해야 하고, SnapShot에서 Section, Item을 주입할 수 있다.

이렇게 되면 위 결과 화면처럼 잘 표시되는 것을 확인할 수 있다.

 

 

 

GitHub - JustHm/DailyRecord: 당일만 기록할 수 있는 일기장 어플

당일만 기록할 수 있는 일기장 어플. Contribute to JustHm/DailyRecord development by creating an account on GitHub.

github.com

 

728x90
Comments