iyOmSd/Title: RxSwift

[RxSwift] RxDataSources을 이용한 TableView구현하기

냄수 2020. 1. 21. 15:12
반응형

지금 까지 기본적인 입출력만 binding 하는것을 해봤어요

약간 더 어려운 

Rx를 이용한 TableView를 구현해 볼거에요

앞의 내용을 하면서 Rx를 조금이라도 이해 했다면 그리 많이 어렵지 않을 거에여!!

 

우선 

 

Pod을 추가해주세요

 

많은 레퍼런스를 보면 이런 방식은 똑같이 하더라구요

그래서 저도 똑같이 했어요

 

Cell에 들어갈 데이터 타입을 정해야겠죠?

 

 

 

 

AnimatableSectionModelType 이건 음... 잘모르겠지만 SectionModel로 사용하기위해서

상속받아 사용하는 거같아요...

상속을 받으면 Identity과 Item를 정의하는게 나올거에요!!

Item은 테이블안에 들어갈 데이터의 타입으로 하고

identity는 연산 프로퍼티로 바꿨어요

 

MySection타입 객체 하나가 섹션 하나가 될거에요

header는 섹션의 title느낌

items는 섹션의 row느낌 입니다!!

 

이렇게 해주면 테이블뷰에 들어갈 데이터타입정의가 끝났어요!!

 

이제 Rx로 TableView를 구현해볼게요

 

저는 Xib파일로 TableViewCell를 생성했어요

 

 

아래는 DataSource를 정의 하는 코드입니다

configureCell의 파라미터로 4가지가 들어오고

그파라미터를 이용해서 보통 TableView의 DataSource를 정의하듯이 하면되요

 

 

섹션에 글자도 달아줄 수 있고

오른쪽 인디케이터쪽에 섹션단어를 보여줄 수 있어요

 

 

딜리게이트를 사용하기위해 설정해주고

 

 

‼️데이터 바인딩 해주기‼️

위의코드는 viewDidLoad위에 초기화한 클래스 맴버변수 입니다!!

 

subject에 data를 넣어두고 변하면 reload되는 형식이에요

 

지금까지 써왔던 BehaviorSubject와는 달리

이번에는 BehaviorRelay를 사용해요!!

 

뭐가다르냐면...

Relay는 Subject를 Wrapping한 녀석이에요...! 라고 써있어요

 

error와 complete가 없어요!!

dispose될때까지 계속 작동한다는 얘기죠

 

UI이벤트를 사용하는경우에 Relay가 적합하다고 하네요

 

함수의 차이도 있어요

onNext대신 accept를 이용해요

 

 

버튼을 누를때 마다 추가되도록

배열에 값을 추가하고 accept를 시켜서 갱신되도록 구현했어요

 

 

셀마다 크기를 다르게 하기위해서

앞에서 사용한 dataSource를 이용해서 셀높이를 조절했어요

 

여기까지 간단하게 RxDataSource로 기본적인 TableView를 만들었어요!!!

 

 

아래는 전체코드입니다!!

import UIKit
import RxCocoa
import RxSwift
import RxDataSources

struct MySection {
    var header: String
    var items: [Item]
}

// section Model
extension MySection: AnimatableSectionModelType {
    typealias Item = Int
    
    init(original: MySection, items: [Int]) {
        self = original
        self.items = items
    }
    
    var identity: String {
        return header
    }
}

class ViewController: UIViewController {
    
    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var random: UIButton!

    let disposeBag = DisposeBag()
    var dataSource: RxTableViewSectionedReloadDataSource<MySection>!
    var sections = [
        MySection(header: "A", items: [1, 2, 3]),
        MySection(header: "B", items: [4, 5])
    ]
    private var subject: BehaviorRelay<[MySection]> = BehaviorRelay(value: [])
    override func viewDidLoad() {
        super.viewDidLoad()

        // tableView 정의
        let nib = UINib(nibName: "TestTableViewCell", bundle: nil)
        tableView.register(nib, forCellReuseIdentifier: "TestCell")
        
        // dataSource 정의
        let dataSource = RxTableViewSectionedReloadDataSource<MySection>(
            configureCell: { dataSource, tableView, indexPath, item in
                let cell = tableView.dequeueReusableCell(withIdentifier: "TestCell", for: indexPath) as! TestTableViewCell
                cell.selectionStyle = .none
                cell.testLabel?.text = "Item \(item)"
                return cell
        })
        
        // 처음값 초기화
        subject.accept(sections)
        
        //섹션 문자
        dataSource.titleForHeaderInSection = { ds, index in
            return ds.sectionModels[index].header
        }
        
        //오른쪽 인디케이터 문자
        dataSource.sectionIndexTitles = { ds in
            return ds.sectionModels.map { $0.header }
        }
        
        //오른쪽 인디케이터 문자와 인덱스참조가능
        dataSource.sectionForSectionIndexTitle = { ds, title, index in
            print(title)
            print(index)
            return ds.sectionModels.map { $0.header }.firstIndex(of: title) ?? 0
        }
        
        self.dataSource = dataSource
        
        // delegate 사용을 위한 선언
        tableView.rx.setDelegate(self)
            .disposed(by: disposeBag)
        
        // binding
        subject
            .bind(to: tableView.rx.items(dataSource: dataSource))
            .disposed(by: disposeBag)
        
    }
    
    @IBAction func randomAction(_ sender: Any) {
        sections.append(MySection(header: "C", items: [6, 7, 8]))
        sections.append(MySection(header: "D", items: [4, 5]))
        subject.accept(sections)
    }
}

extension ViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        guard let item = dataSource?[indexPath], dataSource?[indexPath.section] != nil else { return 0.0 }
        
        return CGFloat(40 + item * 10)
    }
}
반응형