Collection View
UICollectionViewDelegate, UICollectionViewDataSource
class ViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(UINib(nibName: "CollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "CollectionViewCell")
}
}
// MARK: - 기존 CollectionView를 사용하는 방법
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as? CollectionViewCell else {
return UICollectionViewCell()
}
cell.label.text = indexPath.row.description
return cell
}
}
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let itemSpacing: CGFloat = 10
let width: CGFloat = (collectionView.bounds.width - itemSpacing) / 2
let height: CGFloat = width * 10/7
return CGSize(width: width, height: height)
}
}
Modern Collection View (iOS 13.0+)
- 기존의
collectionView
형태에서는 UI와DataSource
의 버전(truth
)가 같지 않을때 멈추는 문제가 있었다.reloadData()
로 해결할 수 있지만, 그럴 경우 애니메이션이 씹히는 현생이 발생. 이를 해결하기 위해snapShot
이라는 새로운 개념을 접근하는 것이 개요.
diffable
은 화면에 보여질 아이템이 업데이트 될 때 마다 collectionView가 자동으로 업데이트 된다는 의미. 이전 데이터와의 차이를 계산하고 애니메이션을 통해 보여준다.
UICollectionViewDiffableDataSource
func setUpDataSource() {
dataSource = UICollectionViewDiffableDataSource<Int, Int>(collectionView: collectionView) { (collectionView, indexPath, cellNumber) -> UICollectionViewCell? in
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as? CollectionViewCell else {
return UICollectionViewCell()
}
cell.label.text = cellNumber.description
return cell
}
}
func performCell() {
let cellNum: [Int] = (0..<10).map{ Int($0) }
var snapShot = NSDiffableDataSourceSnapshot<Int, Int>()
snapShot.appendSections([0])
snapShot.appendItems(cellNum)
self.dataSource.apply(snapShot)
}
UICollectionViewLayout
UICollectionViewFlowLayout
- 기존의 방법대로
UICollectionViewCompositionalLayout (iOS 13.0 +)
전체 코드
class ViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
var dataSource: UICollectionViewDiffableDataSource<Int, Int>!
override func viewDidLoad() {
super.viewDidLoad()
// collectionView.delegate = self
// collectionView.dataSource = self
collectionView.register(UINib(nibName: "CollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "CollectionViewCell")
// UICollectionViewDiffableDataSource를 사용한 방법
collectionView.collectionViewLayout = createLayout()
setUpDataSource()
performCell()
}
}
// MARK: - 기존 CollectionView를 사용하는 방법
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as? CollectionViewCell else {
return UICollectionViewCell()
}
cell.label.text = indexPath.row.description
return cell
}
}
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let itemSpacing: CGFloat = 10
let width: CGFloat = (collectionView.bounds.width - itemSpacing) / 2
let height: CGFloat = width * 10/7
return CGSize(width: width, height: height)
}
}
// MARK: - UICollectionViewDiffableDataSource을 사용한 방법
extension ViewController {
func setUpDataSource() {
dataSource = UICollectionViewDiffableDataSource<Int, Int>(collectionView: collectionView) { (collectionView, indexPath, cellNumber) -> UICollectionViewCell? in
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as? CollectionViewCell else {
return UICollectionViewCell()
}
cell.label.text = cellNumber.description
return cell
}
}
func performCell() {
let cellNum: [Int] = (0..<10).map{ Int($0) }
var snapShot = NSDiffableDataSourceSnapshot<Int, Int>()
snapShot.appendSections([0])
snapShot.appendItems(cellNum)
self.dataSource.apply(snapShot)
}
func createLayout() -> UICollectionViewLayout {
let layout: UICollectionViewCompositionalLayout = {
let itemSpacing: CGFloat = 10
let width: CGFloat = 1.0 / 2.0
let height: CGFloat = 1.0 / 2.5
// item
let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(width),
heightDimension: .fractionalHeight(1)
)
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = NSDirectionalEdgeInsets(top: itemSpacing, leading: itemSpacing, bottom: itemSpacing, trailing: itemSpacing)
// group
let groupSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1),
heightDimension: .fractionalHeight(height)
)
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
//section
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(top: itemSpacing, leading: itemSpacing, bottom: itemSpacing, trailing: itemSpacing)
return UICollectionViewCompositionalLayout(section: section)
}()
return layout
}
}
Reference
- Documents
- collectionView layout
- Modern Collection View
- UICollectionViewLayout
Uploaded by N2T