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
- combine
- 리팩토링
- 클린 코드
- Xcode
- clean architecture
- swiftUI
- map
- rxswift
- collectionview
- RxCocoa
- Human interface guide
- UITextView
- 리펙터링
- Clean Code
- Observable
- HIG
- 애니메이션
- 스위프트
- tableView
- 리펙토링
- swift documentation
- MVVM
- ios
- Refactoring
- ribs
- UICollectionView
- uiscrollview
- SWIFT
- uitableview
- Protocol
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] RxDataSources로 TableView 구현 방법 (RxTableViewSectionedReloadDataSource) 본문
iOS 응용 (swift)
[iOS - swift] RxDataSources로 TableView 구현 방법 (RxTableViewSectionedReloadDataSource)
jake-kim 2021. 12. 1. 22:31* Section별, Item별로 dataSource 사용 모델 구현 패턴 방법은 이곳 참고
Cocoa Pods 종속성
pod 'RxSwift'
pod 'RxCocoa'
pod 'RxDataSources'
모델 정의
- RxTableViewSectionedReloadDataSource를 사용하여 DataSource에서 사용되는 데이터형은 `AnimatableSectionModelType`를 준수하는 모델이어야 가능
- AnimatableSectionModelType은 SectionModelType을 준수하는 프로토콜
- original에 해당되는 모델은 Section에 해당되고, items에 해당되는 인수는 rows값
- AnimatableSectionModelType을 준수하는 모델 정의하기 전에 [Item] 타입에 들어갈 row 모델 먼저 정의
- Item은 IdentifiableType, Equatable을 준수하는 모델
import RxDataSources
struct MyModel {
var message: String
var isDone: Bool = false
}
extension MyModel: IdentifiableType, Equatable {
var identity: String {
// 예제 편의를 위해 UUID().uuidString을 사용했고, 실제에서는 데이터를 구분하는 id값 사용을 지향
return UUID().uuidString
}
}
- AnimatableSectionModelType을 준수하는 모델 정의
struct MySection {
var headerTitle: String
var items: [Item]
}
extension MySection: AnimatableSectionModelType {
typealias Item = MyModel
var identity: String {
return headerTitle
}
init(original: MySection, items: [MyModel]) {
self = original
self.items = items
}
}
RxTableViewSectionedReloadDataSource 구현
- ViewController 준비
import UIKit
import RxDataSources
import RxCocoa
import RxSwift
class ViewController: UIViewController {
}
- Section 데이터 준비
var sections = [
MySection(headerTitle: "첫 번째", items: [MyModel(message: "jake"), MyModel(message:"jake의 iOS")]),
MySection(headerTitle: "두 번째", items: [MyModel(message:"jake의 iOS앱 개발 알아가기"), MyModel(message:"jake의 iOS앱 개발 알아가기\njake의 iOS앱 개발 알아가기")]),
MySection(headerTitle: "세 번째", items: [MyModel(message:"jake의 iOS앱 개발 알아가기\njake의 iOS앱 개발 알아가기\njake의 iOS앱 개발 알아가기\njake의 iOS앱 개발 알아가기")])
]
- RxTableViewSectionedReloadDataSource 구현
- 주의) 해당 코드에서는 row 데이터 (item)만 적용가능
- section데이터는 dataSource.titleForHeaderInSection으로 따로 설정
/// row 데이터 적용 (section은 dataSource.titleForHeaderInSection으로 설정)
var dataSource = RxTableViewSectionedReloadDataSource<MySection> { dataSource, tableView, indexPath, item in
let cell = tableView.dequeueReusableCell(withIdentifier: MyTableViewCell.className, for: indexPath) as! MyTableViewCell
cell.bind(mySectionItem: item)
return cell
}
- section 데이터 반영
// viewDidLoad()에서 호출
private func setupTableViewDataSource() {
dataSource.titleForHeaderInSection = { dataSource, index in
return dataSource.sectionModels[index].headerTitle
}
}
- TableView에 CustomCell 등록, delegate 할당
- dynamic cell 구현은 동적인 Custom View, Custom Cell 구현 방법 참고
// viewDidLoad()에서 호출
private func setupViews() {
let myTableViewCellNib = UINib(nibName: MyTableViewCell.className, bundle: nil)
tableView.register(myTableViewCellNib, forCellReuseIdentifier: MyTableViewCell.className)
tableView.rx.setDelegate(self)
.disposed(by: disposeBag)
}
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
// RxTableViewSectionedReloadDataSource는 일반 dataSourec배열처럼 index로 접근 가능
let data = dataSource[indexPath]
let myModel = MyModel(message: data.message)
let calculatedHeight = MyTableViewCell.height(width: tableView.layer.frame.size.width, myModel: myModel)
return calculatedHeight
}
}
- BehaviorRelay를 이용하여 tableView에 데이터 소스 맵핑
var sectionSubject = BehaviorRelay(value: [MySection]())
...
// viewDidLoad()에서 호출
private func setupTableViewDataSource() {
sectionSubject.accept(sections) // <-
dataSource.titleForHeaderInSection = { dataSource, index in
return dataSource.sectionModels[index].headerTitle
}
}
...
// viewDidLoad()에서 호출
private func bindSubject() {
sectionSubject
.bind(to: tableView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
}
* 전체 소스 코드: https://github.com/JK0369/ExDynamicHeightCell/tree/RxDataSource
제네릭을 사용한 Section 모델 정의
- RxDataSources내부에 존재하는 SectionModel<Section, ItemType>모델을 사용
- 사용할 모델 정의
- ViewController에서 MySection으로 접근하여 사용
import RxDataSources
public typealias MySection = SectionModel<String, MyModel>
public struct MyModel {
var message: String
var isDone: Bool = false
}
extension MyModel: IdentifiableType {
public var identity: String {
return UUID().uuidString
}
}
- ViewController에서 쓰임
var sections = [
MySection(model: "첫 번째", items: [MyModel(message: "jake"), MyModel(message:"jake의 iOS")]),
MySection(model: "두 번째", items: [MyModel(message:"jake의 iOS앱 개발 알아가기"), MyModel(message:"jake의 iOS앱 개발 알아가기\njake의 iOS앱 개발 알아가기")]),
SectionModel(model: "세 번째", items: [MyModel(message:"jake의 iOS앱 개발 알아가기\njake의 iOS앱 개발 알아가기\njake의 iOS앱 개발 알아가기\njake의 iOS앱 개발 알아가기")])
]
/// row 데이터 적용 (section은 dataSource.titleForHeaderInSection으로 설정)
var dataSource = RxTableViewSectionedReloadDataSource<MySection> { dataSource, tableView, indexPath, item in
let cell = tableView.dequeueReusableCell(withIdentifier: MyTableViewCell.className, for: indexPath) as! MyTableViewCell
cell.bind(model: item)
return cell
}
...
* 전체 소스 코드: https://github.com/JK0369/ExDynamicHeightCell
cf) ReactorKit + RxDataSource 편리하게 사용 방법: https://ios-development.tistory.com/796
* 참고
- https://github.com/RxSwiftCommunity/RxDataSources
'iOS 응용 (swift)' 카테고리의 다른 글
Comments