안녕하세요~!
오늘은 rxCocoa를 사용해 UITableView
에 데이터를 어떻게 뿌려주는지! 그 방법들에 대해 알아보겠습니다🧐
단일 데이터를 UILabel
이나 UITextView
에 바인드 시켜주는건 그렇게 어렵지 않았는데요.
그런데 UITableView, UITableViewDataSource의 메서드들은 Observable과 어떻게 엮어서 사용해야할지...?!?!? 는 전혀 감이 안잡히더라구요.
찾아보니 rxCocoa
와 rxDataSource
를 사용하면 간편하게 바인드할 수 있었는데요, 그중에서도 rxCocoa
의 메서드에 대해 공부해보았습니다.
rxCocoa
에서는 3가지 메서드를 제공해주는데, 그 중 2개 메서드의 사용방법과 차이점에 대해 알아보도록 하겠습니다.
🌟 rxCocoa 소스 코드
https://github.com/ReactiveX/RxSwift/blob/main/RxCocoa/iOS/UITableView%2BRx.swift
public func items<Sequence: Swift.Sequence, Source: ObservableType>
(_ source: Source)
-> (_ cellFactory: @escaping (UITableView, Int, Sequence.Element) -> UITableViewCell)
-> Disposable
where Source.Element == Sequence {
return { cellFactory in
let dataSource = RxTableViewReactiveArrayDataSourceSequenceWrapper<Sequence>(cellFactory: cellFactory)
return self.items(dataSource: dataSource)(source)
}
}
중첩된 구조의 메서드 형태입니다.
각 파라미터의 역할입니다.
source
: Observable sequence of items
cellFactory
: element
를 이용해 cell
을 configure
Disposable
을 리턴합니다.
구현은 복잡한데 사용은 어렵지 않습니다🙂
먼저 테이블 뷰 셀에 제공할 데이터 배열을 Observable로 만들어줍니다.
let items = Observable.just([
"First Item",
"Second Item",
"Third Item"
])
items.bind(to: _)
에 바인딩할테이블뷰.rx.items
를 넣고,
메서드의 후행클로저에 tableView
, row
, element
를 이용해서 cell을 configure해줍니다.
items.bind(to: tableView.rx.items) { (tableView, row, element) in
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")!
cell.textLabel?.text = "\(element) @ row \(row)"
return cell
}
.disposed(by: disposeBag)
후행클로저에서 제공되는 파라미터 중tableView
에는 제가 위에서 넣어준 바인딩할 테이블뷰, row
는 deque될 cell의 row의 Int
값, element
는 데이터 타입(String
)이 들어갑니다.
row
마다 다른 cell을 리턴해주어야할 때 사용할 수 있겠네요
// output: Observable<[ProductViewModel]>
output.products
.bind(to: tableView.rx.items) { (tableView, row, element) in
guard let cell = tableView.dequeueReusableCell(withIdentifier: "ProductTableViewCell") as? ProductTableViewCell else{
return ProductTableViewCell()
}
cell.fill(with: element)
return cell
}
.disposed(by: disposeBag)
두번재 방법은 첫번째 방법보다 더 진화된 방법입니다. 리턴할 Cell
의 타입을 파라미터로 받아서 타입캐스팅 까지 해준 Cell
을 반환합니다.
public func items<Sequence: Swift.Sequence, Cell: UITableViewCell, Source: ObservableType>
(cellIdentifier: String, cellType: Cell.Type = Cell.self)
-> (_ source: Source)
-> (_ configureCell: @escaping (Int, Sequence.Element, Cell) -> Void)
-> Disposable
where Source.Element == Sequence {
return { source in
return { configureCell in
let dataSource = RxTableViewReactiveArrayDataSourceSequenceWrapper<Sequence> { tv, i, item in
let indexPath = IndexPath(item: i, section: 0)
// cellType으로 강제 캐스팅
let cell = tv.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! Cell
configureCell(i, item, cell)
return cell
}
return self.items(dataSource: dataSource)(source)
}
}
}
1번 방법과 동일하게 셀에 바인드할 데이터 배열의 Observable을 준비합니다.
let items = Observable.just([
"First Item",
"Second Item",
"Third Item"
])
1번 방법과 동일하게 첫번째 파라미터에 바인딩할 테이블뷰.rx.item
를 넣고, cellIdentifer
에 Cell Identifier, cellType
에 커스텀 셀 타입을 명시합니다.
그리고 후행 클로저에서 제공된 row
, element
를 사용해 cell
을 configure합니다.
items.bind(to: tableView.rx.items(cellIdentifier: "Cell", cellType: UITableViewCell.self)) { (row, element, cell) in
cell.textLabel?.text = "\(element) @ row \(row)"
}
.disposed(by: disposeBag)
// output: Observable<[ProductViewModel]>
output.products
.bind(to: tableView.rx.items(cellIdentifier: "ProductTableViewCell", cellType: ProductTableViewCell.self)) { (row, element, cell) in
// cell: ProductTableViewCell
cell.fill(with: element)}
.disposed(by: disposeBag)
element
로 셀을 configure, 그리고 cell을 리턴한다. element
로 configue, 그리고 cell을 리턴한다. (단 강제 캐스팅을 하므로 cellType
이 틀리면 크래시남 주의)