Modern Collection View [4] - Edit with Diffable Data Source | Reordering | Swipe Action | Checkmark
Diffable DataSource
를 적용한 컬렉션 뷰의 편집 기능(스와이프 액션, 삭제 등) 적용private var selectedCharacters = Set<Character>()
if self.selectedCharacters.contains(character) {
accessories.append(.checkmark(displayed: .whenNotEditing))
}
collectionView.delegate = self
...
extension CharacterListViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
collectionView.deselectItem(at: indexPath, animated: true)
guard let item = dataSource.itemIdentifier(for: indexPath) else { return }
if selectedCharacters.contains(item) {
selectedCharacters.remove(item)
} else {
selectedCharacters.insert(item)
}
var snapshot = dataSource.snapshot()
snapshot.reloadItems([item])
dataSource.apply(snapshot, animatingDifferences: true)
}
}
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
collectionView.isEditing = editing
}
navigationItem.leftBarButtonItem = editButtonItem
editButtonItem
을 통해 해당 setEditing
여부를 토글var accessories: [UICellAccessory] = [
.delete(displayed: .whenEditing, actionHandler: {
self.deleteCharacter(character)
}),
]
whenEditing
을 통해 어느 상황에서 해당 액세서리를 보여줄지 결정 가능private func deleteCharacter(_ character: Character) {
guard let indexPath = dataSource.indexPath(for: character) else { return }
backingStore[indexPath.section].characters.remove(at: indexPath.item)
selectedCharacters.remove(character)
var snapshot = dataSource.snapshot()
snapshot.deleteItems([character])
dataSource.apply(snapshot, animatingDifferences: true)
}
deleteCharacter
를 통해 삭제된 캐릭터를 전역 변수에서 제거 및 스냅샷에서 제거한 컬렉션 뷰 UI를 그리는 메소드 실행 private lazy var listLayout: UICollectionViewLayout = {
var listConfig = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
listConfig.headerMode = .supplementary
listConfig.trailingSwipeActionsConfigurationProvider = { [weak self] indexPath -> UISwipeActionsConfiguration? in
guard let character = self?.dataSource.itemIdentifier(for: indexPath) else { return nil }
return UISwipeActionsConfiguration(actions: [.init(style: .destructive, title: "Delete", handler: { [weak self] _, _, completion in
self?.deleteCharacter(character)
completion(true)
})])
}
return UICollectionViewCompositionalLayout.list(using: listConfig)
}()
UICollectionLayoutListConfiguration
의 스와이프 액션 핸들러를 통해 위의 삭제 메소드를 호출하는 컴플리션 사용var accessories: [UICellAccessory] = [
.reorder(displayed: .whenEditing)
]
reorder
프로퍼티를 통해 편집 중인 상황에서 특정 셀을 드래그 드롭으로 재정렬할 수 있는 방식 제공dataSource.reorderingHandlers.canReorderItem = { [weak self] character -> Bool in
return (self?.navigationItem.searchController?.searchBar.text ?? "").isEmpty
}
reorderingHandlers
가 제공하는 여러 가지 프로퍼티를 통해 조작 가능canReorderItem
을 통해 어떤 상황에 재정렬이 가능한지 결정 가능dataSource.reorderingHandlers.didReorder = { [weak self] transaction in
guard let self = self else { return }
var backingStore = self.backingStore
for sectionTransaction in transaction.sectionTransactions {
let sectionIdentifier = sectionTransaction.sectionIdentifier
if let sectionIndex = transaction.finalSnapshot.indexOfSection(sectionIdentifier) {
backingStore[sectionIndex].characters = sectionTransaction.finalSnapshot.items
}
}
self.backingStore = backingStore
self.reloadHeaders()
}
didReorder
는 해당 재정렬 드래그 드롭이 실행이 된 이후 들어오는 스냅샷을 통해 실제 컬렉션 뷰에 어떻게 데이터 현황을 보고할지 결정하는 파트finalSnapshot
의 indexOfSection
을 통해 해당 아이템이 어떤 섹션에 들어있는지 체크, 현재 들고 있는 전역 변수 섹션에 새롭게 정렬된 아이템을 그 순서대로 넣고, 그대로 스냅샷을 전역 변수에 반영해당 강의 시리즈 중 가장 궁금했고, 가장 도움이 되는 영상이었다!
Diffable DataSource
를 잘 사용만 한다면 큰 무기가 될 것이다!