-
RxSwift - TableView, CollectionVIew를 사용해보자Ray Wenderlich/RxSwift 2020. 2. 29. 21:28
Ch.18 Table and CollectionViews
iOS앱에서 가장 많이 사용하는 UI는 UITableView, UICollectionVIew를 통해 데이터의 리스트를 표현하는 것이다.
보통은 delegate, dataSource의 콜백을 통해 데이터를 표현한다.
RxSwift를 사용하면 observable sequence를 TableView, CollectionView에 표현할 수 있음을 물론이고, 코드의 양도 줄일 수 있다.
UITableView, UICollectionView의 간단한 사용은 RxCocoa에 이미 포함되어 있다.
좀 더 심화적인 내용(섹션관리, 애니메이션 등)은 RxDataSources(https://github.com/RxSwiftCommunity/RxDataSources)를 통해 사용가능하다.
Basic table view
UITableView를 사용하는 기본적인 시나리오에 대해 살펴보자.
화면에 도시 이름을 리스트로 표현할 것이다.
@IBOutlet var tableView: UITableView!
func bindTableView() {
let cities = Observable.of(["Lisbon", "Copenhagen", "London", "Madrid", "Vienna"])
cities .bind(to: tableView.rx.items) { (tableView: UITableView, index: Int, element: String) in
let cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
cell.textLabel?.text = element
return cell
}.disposed(by: disposeBag)
}
** UITableViewDataSource를 별도로 설정할 필요가 없다.
코드를 이해해보자.
- tableView.rx.items는 observable sequence를 바인딩하는 펑션이다.
- 이 바인딩은 제공된 observable을 subscribe하는 가상의 ObserverType객체를 생성하고, 자신을 스스로 dataSource, delegate로 설정한다.
- 새로운 element의 배열이 observable에 전달되면, 이 바인딩은 테이블뷰를 reload한다.
- 각 item에 해당하는 cell에 접근하기 위해, RxCocoa에 전달한 클로져가 수행된다.
이게 보편적인 사용법이다. 그러면 사용자가 셀을 선택하는 경우는 어떻게 대처할 것인가?
tableView.rx.modelSelected(String.self)
.subscribe(onNext: { model in
print("\(model) was selected")
})
.disposed(by: disposeBag)
사용자의 선택이 발생하면 modelSelected(_:) 메소드가 observable을 리턴한다.
유사한 기능으로 itemSelected()가 있다. (얘는 IndexPath를 전달해준다.)
RxCocoa에서는 많은 observable을 제공한다.
- modelSelected(_:), modelDeselected(_:), itemSelected, itemDeselected
- itemAccessoryButtonTapped
- itemInserted, itemDeleted, itemMove (셀 editMode에서)
- willDisplayCell, didEndDisplayingCell (UITableViewDelegate에 있는 걔들 맞음)
Multiple cell types
위에서는 하나의 셀 종류만 사용했다.
하지만 우리는 보통 여러 종류의 셀을 사용한다.
여러 모델과 셀을 사용하는 방법 중에서, enum을 사용하는 방법을 추천한다.
아무리 많은 셀 타입도 쉽게 관리할 수 있다.
String을 보여주는 셀, 2개의 이미지를 보여주는 셀 총 2개 타입의 셀을 사용한다고 가정해보자.
이에 해당하는 enum을 만들고, Observable을 만들어보자.
enum MyModel {
case text(String)
case pairOfImages(UIImage, UIImage)
}
let observable = Observable<[MyModel]>.just([
.textEntry("Paris"),
.pairOfImages(UIImage(named: "EiffelTower.jpg")!, UIImage(named: "LeLouvre.jpg")!),
.textEntry("London"),
.pairOfImages(UIImage(named: "BigBen.jpg")!, UIImage(named: "BuckinghamPalace.jpg")!) ])
observable.bind(to: tableView.rx.items) { (tableView: UITableView, index: Int, element: MyModel) in
let indexPath = IndexPath(item: index, section: 0)
switch element {
case .textEntry(let title):
let cell = tableView.dequeueReusableCell(withIdentifier: "titleCell", for: indexPath) as! TextCell
cell.titleLabel.text = title
return cell
case .pairOfImages(let firstImage, let secondImage):
let cell = tableView.dequeueReusableCell(withIdentifier: "pairOfImagesCell", for: indexPath) as! ImagesCell
cell.leftImage.image = firstImage
cell.rightImage.image = secondImage
return cell
}
}.disposed(by: disposeBag)
Providing additional functionality
RxCocoa에서 지원하는 기능 외에도 직접 delegate역할을 하고 싶을 수 있다.
ViewController가 delegate역할을 하고 싶을 수 있다. (class ViewController: UICollectionViewDelegateFlowLayout)
예를 들어, UICollectionView에서 collectionView(_:layout:sizeForItemAt:)메소드를 사용하는 경우가 이에 해당한다.
nib또는 스토리보드에서 delegate로 연결시켜준다면 ViewController가 Delegate콜백을 전달 받을 수 있을 것이다.
혹은 이미 컬렉션뷰를 바인딩하였다면 아래 메소드를 사용하여 delegate를 설정하도록 하자. (delegate설정하는 순서가 상관있는듯??)
tableView.rx.setDelegate(myDelegateObject) tableView.delegate = self와 같은 직접적인 delegate설정은 오작동의 원인이 될 수 있다.
RxDataSources
RxCocoa는 테이블뷰, 컬렉션뷰의 많은 기능을 이미 제공하고 있다.
하지만 좀 더 심화 기능을 구현하고 싶을 수 있다. 셀 insert, delete 애니메이션이나 특정 섹션 리로드 등 RxDataSource을 사용하려면 배울것도 할것도 많지만 그만큼 강력한 기능을 제공한다.
https://github.com/RxSwiftCommunity/RxDataSources 를 참고하시길
'Ray Wenderlich > RxSwift' 카테고리의 다른 글
RxSwift - MVC, MVVP with RxSwift (0) 2020.02.29 RxSwift - retryWhen(_:)에 대해서 알아보자 (0) 2020.02.20 RxSwift - amb(_:), switchLatest()에 대해서 알아보자 (0) 2020.02.10 RxSwift - withLastestFrom(_:), sample(_:)에 대해서 알아보자 (0) 2020.02.10 RxSwift - combineLatest(), zip() 에 대해서 알아보자. (1) 2018.07.13