iyOmSd/Title: RxSwift

[RxSwift] RxDataSource TableView Cell타입별 그리기

냄수 2021. 9. 10. 19:35
반응형

하나의 TableView안에

위처럼

다양한 CellType을 적용해야할 때가 존재해요

 

그럴때 RxDataSource를 이용해서는 어떻게 처리하나~

에대해서 한번 알아보겠습니다

 

먼저 CellModel을 만들어볼거에요

// MARK: - Cell Model

struct TestSection {
    var items: [Item]
}

enum TestItem {
    case aCell(ACellModel)
    case bCell(BCellModel)
    case none
}

extension TestSection: SectionModelType {
    typealias Item = TestItem
    
    init(original: TestSection, items: [Item] = []) {
        self = original
        self.items = items
    }
}

struct ACellModel {
    let string: String
}

struct BCellModel {
    let string: String
}

Section이 Item배열을 가지고있고

Item은 enum타입으로 이루어져있어요

enum타입이 곧 Cell의 타입을 구분하기 위함이죠

 

 

다음으로

datasource를 정의해줄거에요

 

items이 [TestItem]타입이니까

item으로 TestItem이나올거고

enum타입이니까 구분해서 사용하면돼요

class RxDataSourceViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!
    private let disposeBag = DisposeBag()
    private lazy var dataSource = RxTableViewSectionedReloadDataSource<TestSection> { [weak self] dataSource, tableview, indexPath, item in
        switch item {
        case let .aCell(cellModel):
            let cell = tableview.dequeueReusableCell(withIdentifier: TestTableViewCell.identifier, for: indexPath) as! TestTableViewCell
            return cell
        case let .bCell(cellModel):
            let cell = tableview.dequeueReusableCell(withIdentifier: TestTableViewCell.identifier, for: indexPath) as! TestTableViewCell
            return cell
        case .none:
            let cell = tableview.dequeueReusableCell(withIdentifier: TestTableViewCell.identifier, for: indexPath) as! TestTableViewCell
            return cell
        }
    }
    private let data = BehaviorRelay<[TestSection]>(value: [])
    
    ...
}

aCell, bCell, none타입에 맞게

원하는 Cell클래스를 만들어서 넣어주면 구분할 수있어요

간단하게 여기서는 하나의 셀로만 구분해서 사용해보도록 할거에요

 

 

어떻게만들어서 적용할지

샘플 데이터를 생성해볼게요

override func viewDidLoad() {
    super.viewDidLoad()
    /* cell register 해줘야합니다 */
   
    tableView.rowHeight = UITableView.automaticDimension

    let item1 = TestItem.aCell(ACellModel(string: "1"))
    let item2 = TestItem.aCell(ACellModel(string: "2"))
    let item3 = TestItem.aCell(ACellModel(string: "3"))
    let item4 = TestItem.bCell(BCellModel(string: "4"))
    let item5 = TestItem.none
    let section1 = TestSection(items: [item1, item2, item3, item4, item5])
    
    data
        .bind(to: tableView.rx.items(dataSource: dataSource))
        .disposed(by: disposeBag)
    
    data.accept([section1])
}

/* cell구성
a
a
a
b
none
*/

한 section에 필요한 item만큼 넣어서 생성해줬구요

 

 

추가적으로

cell의 이벤트를 받아오는 작업을 해보려고해요

 

방법으로

delegate를 사용하는방법

클로져사용하는 방법

rx extension을 사용하는 방법

 

이 있을 것같네요

rx답게 사용하기위해 rx를 이용해볼거구요

 

간단한 동작을 구현해볼거에요

 

Cell안에 버튼이있고

버튼을 누르면 버튼의 크기를 +20씩 늘릴거에요

하지만 이렇게 늘리기만하면 적용이안돼서

Cell크기가 그대로일거니까

이벤트를 전달해서 ViewController에 업데이트하는 코드도 추가할거에요

class TestTableViewCell: UITableViewCell {
    static let identifier = "TestCell"
    
    @IBOutlet weak var heightLayout: NSLayoutConstraint!
    @IBOutlet weak var button: UIButton!
    private let disposeBag = DisposeBag()
    
    func bind() {
    // 버튼클릭시 크기 +20, 버튼타이틀을 크기숫자로 변경
        button.rx.tap
            .subscribe(onNext: { [weak self] in
                let height: CGFloat = (self?.heightLayout.constant ?? 0) + 20
                print(height)
                self?.heightLayout.constant = height
                self?.button.setTitle("\(height)", for: .normal)
            })
            .disposed(by: disposeBag)
    }
    
    func aCell() {
        button.setTitle("ACell", for: .normal)
    }
    
    func bCell() {
        button.setTitle("BCell", for: .normal)
    }
    
    func noneCell() {
        button.setTitle("NoneCell", for: .normal)
    }
}

// Rx

extension Reactive where Base: TestTableViewCell {
    var tapButton: ControlEvent<Void> {
        ControlEvent(events: base.button.rx.tap)
    }
}

이 셀의 이벤트를 실행하기위해

 

datasource코드를 조금 수정해야겠네요

private lazy var dataSource = RxTableViewSectionedReloadDataSource<TestSection> { [weak self] dataSource, tableview, indexPath, item in
    switch item {
    case let .aCell(cellModel):
        let cell = tableview.dequeueReusableCell(withIdentifier: TestTableViewCell.identifier, for: indexPath) as! TestTableViewCell
        cell.bind()
        cell.aCell()
        cell.rx.tapButton
            .subscribe(onNext: {
                tableview.performBatchUpdates(nil)
            })
        return cell
        
        ...
    }

초기상태는 ACell 텍스트가 뜨고

버튼을 누를때마다 크기+20가 되면서 텍스트에 버튼크기가 노출됩니다

cell.rx.tapButton -> 위에서 extension해서 이렇게 이벤트를 뷰컨트롤러에서 컨트롤할 수 있습니다!

 

반응형