Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- ios
- Refactoring
- 클린 코드
- collectionview
- 리팩토링
- 애니메이션
- 스위프트
- MVVM
- swift documentation
- uiscrollview
- Clean Code
- rxswift
- Protocol
- HIG
- UICollectionView
- 리펙토링
- SWIFT
- map
- tableView
- combine
- 리펙터링
- Observable
- Human interface guide
- ribs
- swiftUI
- RxCocoa
- clean architecture
- Xcode
- uitableview
- UITextView
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] 2. Diffable Data Source - UICollectionViewDiffableDataSource, UICollectionViewCompositionalLayout (컬렉션 뷰) 본문
iOS 응용 (swift)
[iOS - swift] 2. Diffable Data Source - UICollectionViewDiffableDataSource, UICollectionViewCompositionalLayout (컬렉션 뷰)
jake-kim 2021. 10. 2. 22:351. Diffable Data Source - UITableViewDiffableDataSource (테이블 뷰)
2. Diffable Data Source - UICollectionViewDiffableDataSource (컬렉션 뷰)
UICollectionViewDiffableDataSource
- TableView에서의 Diffable Data Source와 같은 원리로 iOS 13+부터 사용가능
- 개념은 TableView에서의 Diffable Data Source를 참고하고, 아래에서는 예제 코드로 확인
UICollectionViewCompositionalLayout
- UICollectionViewLayout의 서브클래스이며 compositinoal하게 레이아웃을 쉽게 적용하기 위해서 등장
- 개념 - compositinoal
- collectionView의 레이아웃을 이루는 객체들 Item, Group, Section에 각각 레이아웃정보를 주고, 그 객체들을 합쳐서 구성
- 사용방법: Item 안쪽에서부터, Group, Section 순으로 바깥쪽 레이아웃속성을 지정해주어서 적용
- NSCollectionLayoutSize 생성 > Item, Group, Section 객체의 생성자에 적용
- NSCollectionLayoutItem
- NSCollectionLayoutGroup
- NSCollectionLayoutSection
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
}
- 크기 속성
- absolute: 고정 크기
- fractionalHeight, fractinoalWidth: 비율 크기
- estimated: 최소 크기
예제) Diffable Data Source
- tableView에서 Diffable Data Source의 개념과 동일하게 구현
- dataSource 정의, cell 모양 정의
// PhotoViewModel2.swift
var dataSource: UICollectionViewDiffableDataSource<Section, Photo>!
// PhotoViewController2.swift
viewModel.dataSource = UICollectionViewDiffableDataSource<Section, Photo>(collectionView: collectionView,
cellProvider: { [weak self] collectionView, indexPath, photo in
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PhotoCollectionViewCell.identifier, for: indexPath)
self?.viewModel.loadImages(for: photo)
(cell as? PhotoCollectionViewCell)?.model = photo
return cell
})
- ViewModel에서 snapshot으로 데이터 업데이트
// loadData() 메소드
var snapshot = weakSelf.dataSource.snapshot()
if snapshot.sectionIdentifiers.isEmpty {
snapshot.appendSections([.main])
}
snapshot.appendItems(responseDTO.toDomain())
DispatchQueue.global(qos: .background).async {
weakSelf.dataSource.apply(snapshot, animatingDifferences: false)
}
// loadImages() 메소드
photo.image = image
var snapshot = `self`.dataSource.snapshot()
guard snapshot.indexOfItem(photo) != nil else { return }
snapshot.reloadItems([photo])
DispatchQueue.global(qos: .background).async {
`self`.dataSource.apply(snapshot, animatingDifferences: false)
}
예제) Compositional Layout - 왼쪽에 1개의 Item, 오른쪽에 3개의 Item
- 준비 - customCollectionViewCell을 사용한다면, UIImageView의 속성 값 지정이 필요
photoImageView.contentMode = .scaleToFill
photoImageView.clipsToBounds = true
- 2개의 group으로 이루어진 layout
- 설계
- 구현
- Group은 총 2개 존재
- 왼쪽 그룹은 item이 1개 존재
- 오른쪽 그룹은 item이 3개 존재: item 3개 모두 크기가 동일하다고하면 item하나만 정의한다음 Group객체에 item객체 하나를 넣고 count:3으로 부여
// UICollectionViewLayout.swift
extension UICollectionViewLayout {
// 이곳에다 구현
}
// 사용하는 곳
// PhotoViewController2.swift
class PhotoViewController2: UIViewController {
private lazy var collectionView: UICollectionView = {
let view = UICollectionView(frame: view.bounds, collectionViewLayout: .leftThreeRightThree) // <- `.leftThreeRightThree`와 같이 사용
...
}
}
- 주의 사항
- item의 fraction은 해당 item을 감싸고 있는 group에 의존받으므로, Cell의 콘텐트 사이즈를 변경하려면 group의 fraction값을 조정하여 변경해야 가능 (ex - item의 fraction을 0.3으로 주면 0.7만큼 그룹 내의 빈 공간이 생기는것 주의)
- 각 item의 contentInsets값은 group의 생성자에 입력되기 전에 적용해주어야 적용되며, group이 만들어지고난 후에 적용하면 미적용
// leftOneRightThree
static let leftOneRightThree = UICollectionViewCompositionalLayout { section, environment in
let margin = 1.0
// 좌측 그룹
let leadingItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0))
let leadingItem = NSCollectionLayoutItem(layoutSize: leadingItemSize)
leadingItem.contentInsets = NSDirectionalEdgeInsets(top: margin,
leading: margin,
bottom: margin,
trailing: margin)
let leadingGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5),
heightDimension: .fractionalHeight(1.0))
let leadingGroup = NSCollectionLayoutGroup.vertical(layoutSize: leadingGroupSize,
subitem: leadingItem,
count: 1)
// 우측 그룹
let trailingItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0))
let trailingItem = NSCollectionLayoutItem(layoutSize: trailingItemSize)
trailingItem.contentInsets = NSDirectionalEdgeInsets(top: margin,
leading: margin,
bottom: margin,
trailing: margin)
let trailingGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5),
heightDimension: .fractionalHeight(1.0))
let trailingGroup = NSCollectionLayoutGroup.vertical(layoutSize: trailingGroupSize,
subitem: trailingItem,
count: 3)
// 좌측 그룹과 우측 그룹을 포함하는 그룹
let containerGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(0.6))
let containerGroup = NSCollectionLayoutGroup.horizontal(layoutSize: containerGroupSize,
subitems: [leadingGroup, trailingGroup])
let section = NSCollectionLayoutSection(group: containerGroup)
return section
}
예제) Compositional Layout - 왼쪽에 3개의 Item, 오른쪽에 3개의 Item
- 2개의 그룹으로 이루어진 layout
- 설계
- item의 fraction height값이 모두 다르므로, item 6개 모두 생성하고 각 fraction height값은 위와 같이 각 group에 속하는 item fraction height 합이 1이 되도록 설계
static let leftThreeRightThree = UICollectionViewCompositionalLayout { section, environment in
let margin = 2.0
// 좌측 그룹
/// first item
let leadingFirstItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(0.35))
let leadingFirstItem = NSCollectionLayoutItem(layoutSize: leadingFirstItemSize)
leadingFirstItem.contentInsets = NSDirectionalEdgeInsets(top: margin, leading: margin, bottom: margin, trailing: margin)
/// second item
let leadingSecondItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(0.2))
let leadingSecondItem = NSCollectionLayoutItem(layoutSize: leadingSecondItemSize)
leadingSecondItem.contentInsets = NSDirectionalEdgeInsets(top: margin, leading: margin, bottom: margin, trailing: margin)
/// third item
let leadingThirdItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(0.45))
let leadingThirdItem = NSCollectionLayoutItem(layoutSize: leadingThirdItemSize)
leadingThirdItem.contentInsets = NSDirectionalEdgeInsets(top: margin, leading: margin, bottom: margin, trailing: margin)
/// leading group
let leadingGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5),
heightDimension: .fractionalHeight(1.0))
let leadingGroup = NSCollectionLayoutGroup.vertical(layoutSize: leadingGroupSize,
subitems: [leadingFirstItem, leadingSecondItem, leadingThirdItem])
// 우측 그룹
/// first item
let trailingFirstItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(0.5))
let trailingFirstItem = NSCollectionLayoutItem(layoutSize: trailingFirstItemSize)
trailingFirstItem.contentInsets = NSDirectionalEdgeInsets(top: margin, leading: margin, bottom: margin, trailing: margin)
/// second item
let trailingSecondItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(0.2))
let trailingSecondItem = NSCollectionLayoutItem(layoutSize: trailingSecondItemSize)
trailingSecondItem.contentInsets = NSDirectionalEdgeInsets(top: margin, leading: margin, bottom: margin, trailing: margin)
/// third item
let trailingThirdItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(0.3))
let trailingThirdItem = NSCollectionLayoutItem(layoutSize: trailingThirdItemSize)
trailingThirdItem.contentInsets = NSDirectionalEdgeInsets(top: margin, leading: margin, bottom: margin, trailing: margin)
/// trailing group
let trailingGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5),
heightDimension: .fractionalHeight(1.0))
let trailingGroup = NSCollectionLayoutGroup.vertical(layoutSize: trailingGroupSize,
subitems: [trailingFirstItem, trailingSecondItem, trailingThirdItem])
/// margin
[trailingFirstItem, trailingSecondItem, trailingThirdItem].forEach {
$0.contentInsets = NSDirectionalEdgeInsets(top: margin, leading: margin, bottom: margin, trailing: margin)
}
// 좌측 그룹과 우측 그룹을 포함하는 그룹
let containerGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0))
let containerGroup = NSCollectionLayoutGroup.horizontal(layoutSize: containerGroupSize,
subitems: [leadingGroup, trailingGroup])
let section = NSCollectionLayoutSection(group: containerGroup)
return section
}
dataSource를 비우고, cell에도 모두 초기화 시키는 방법
- 빈 snapshot을 만든 후, 기존 dataSource에 apply 실행
let snapshot = NSDiffableDataSourceSnapshot<Section, Photo>.init()
dataSource.apply(snapshot)
* 전체 코드: https://github.com/JK0369/PaginationExample
* 참고
- UICollectionViewDiffableDataSource: https://developer.apple.com/documentation/uikit/uicollectionviewdiffabledatasource
- Implementing Modern Collection Views:
- Compositional Layout:
https://www.raywenderlich.com/5436806-modern-collection-views-with-compositional-layouts
- UICollectionViewCompositionalLayout
https://developer.apple.com/documentation/uikit/uicollectionviewcompositionallayout
'iOS 응용 (swift)' 카테고리의 다른 글
Comments