순진이의 하루/study정리
21. UITableViewDiffableDataSource
순진이
2023. 1. 3. 20:42
AViewModel과 UITableVIewDiffableDataSource 활용하기
※ 스토리보드로 진행
ViewController
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
private let viewModel = ViewModel()
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
viewModel.makeTableView(tableView: tableView)
}
}
ViewModel
- custom cell일 경우, register해주는 작업도 필요
- 반드시 append 해주는 작업을 해야 dataSource로 쓸 수 있음
import UIKit
import Combine
class ViewModel {
private var dataSource: UITableViewDiffableDataSource<SectionClass, Int>!
//<Int, Int> -> Section과 row라고 생각해주면 됨, hasable한 값이 들어가야 함
let sectionClasses = [SectionClass(name: "1"), SectionClass(name: "2")]
var numbers = [1, 2, 3]
var newNumbers = [4, 5, 6]
func addNewDate() {
updateSnapshot2(items: numbers + newNumbers, section: sectionClasses[1])
}
private func updateSnapshot(items: [Int], section: SectionClass) {
var snapshot = NSDiffableDataSourceSnapshot<SectionClass, Int>()
snapshot.appendSections(sectionClasses)
snapshot.appendItems(items, toSection: section)
dataSource.apply(snapshot)
}
func makeTableView(tableView: UITableView) {
let nib = UINib(nibName: CustomTableViewCell.identifier, bundle: nil)
tableView.register(nib, forCellReuseIdentifier: CustomTableViewCell.identifier)
dataSource = .init(tableView: tableView, cellProvider: { tableView, indexPath, itemIdentifier in
guard let cell = tableView.dequeueReusableCell(withIdentifier: CustomTableViewCell.identifier, for: indexPath) as? CustomTableViewCell else { fatalError() }
cell.configure(text: String(itemIdentifier))
return cell
})
updateSnapshot(items: numbers, section: sectionClasses[0])
}
}
struct SectionClass: Hashable {
let name: String
func hash(into hasher: inout Hasher) {
hasher.combine(name)
}
}
0. UITableViewDiffableDataSource 선언
@preconcurrency @MainActor class UITableViewDiffableDataSource<SectionIdentifierType, ItemIdentifierType> : NSObject where SectionIdentifierType : Hashable, SectionIdentifierType : Sendable, ItemIdentifierType : Hashable, ItemIdentifierType : Sendable
- UITableViewDiffableDataSource<> 안에 들어있는 type은 hasable 해야 함
- UITableViewDiffableDataSource<A, B>인 경우 -> A가 section, B가 Item이라고 생각하면 됨.
1. Section과 Item을 추가
1-1. Section을 추가하는 방법 - enum로 만들기
- enum 자체가 hasable하기 때문에 매우 유용
enum Section: CaseIterable {
case first
case second
}
- UITableViewDiffableDataSource<>안의 타입을 맞춰줘야 함 (여기서는 enum이므로 Section 타입)
//선언부
private var dataSource: UITableViewDiffableDataSource<Section, Int>!
//사용부
private func updateSnapshot(items: [Int], section: Section) {
var snapshot = NSDiffableDataSourceSnapshot<Section, Int>()
snapshot.appendSections(Section.allCases)
snapshot.appendItems([1, 2, 3], toSection: Section.first)
snapshot.appendItems([4, 5, 6], toSection: Section.second)
dataSource.apply(snapshot)
}
1-2. Section을 추가하는 방법 - struct로 만들기(현재 코드)
- struct는 hasable 하지 않기 때문에 hash 함수를 구현해줘야 하
struct SectionClass: Hashable {
let name: String
func hash(into hasher: inout Hasher) {
hasher.combine(name)
}
}
//선언부
private var dataSource: UITableViewDiffableDataSource<SectionClass, Int>!
let sectionClasses = [SectionClass(name: "1"), SectionClass(name: "2")]
//구현부
private func updateSnapshot2(items: [Int], section: SectionClass) {
var snapshot = NSDiffableDataSourceSnapshot<SectionClass, Int>()
snapshot.appendSections(sectionClasses)
snapshot.appendItems(items, toSection: section)
dataSource.apply(snapshot)
}
2. dataSource 연결
- cellProvider를 클로저로 전달할 수도 있음
dataSource = .init(tableView: tableView, cellProvider: { tableView, indexPath, itemIdentifier in
guard let cell = tableView.dequeueReusableCell(withIdentifier: CustomTableViewCell.identifier, for: indexPath) as? CustomTableViewCell else { fatalError() }
cell.configure(text: String(itemIdentifier))
return cell
})
CustomCell
- nib 파일 통해 label 하나 넣고, 연결해놓기
import UIKit
class CustomTableViewCell: UITableViewCell {
static let identifier = String(describing: CustomTableViewCell.self)
@IBOutlet weak var label: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func configure(text: String) {
label.text = text
}
}
UITableViewDelegate
- UITableViewDiffableDataSource쓸 때, Delegate는 평소랑 똑같이 써주면 된다!
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
private let viewModel = ViewModel()
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
viewModel.setupTableView(tableView: tableView)
}
}
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
viewModel.addNewDate()
}
}
//ViewModel.swift
func addNewDate() {
updateSnapshot2(items: numbers + newNumbers, section: sectionClasses[1])
}