티스토리 뷰

반응형




이렇게 준비를 한다..! 

1. 뷰컨에 TableView를 올려주고 

2. Tableview에 TableViewCell을 올려주고 

3. TableViewCell의 identifier를 NameCell 로 설정해준다 



그리고 RxTableViewController에 RxSwift와 RxCocoa를 import 하고 bind할 준비를 해준다 



>> 총 4가지 방법으로 tableView를 그릴 수 있다 


https://github.com/RxSwiftCommunity/RxDataSources 를 보면 다음과 같이 나와있다


Bind the data to the tableView/collectionView using one of:


  • rx.items(dataSource:protocol<RxTableViewDataSourceType, UITableViewDataSource>)
  • rx.items(cellIdentifier:String)
  • rx.items(cellIdentifier:String:Cell.Type:_:)
  • rx.items(_:_:)



[ 1 ] tableView.rx.items 사용하기 



class RxTableViewController: UIViewController {

    

    @IBOutlet weak var tableView: UITableView!

    let bag = DisposeBag()

    

    override func viewDidLoad() {

        super.viewDidLoad()

        bindTableView()

    }

    

    private func bindTableView() {

        let cities = ["London", "Vienna", "Lisbon"]

        let citiesOb: Observable<[String]> = Observable.of(cities)

        citiesOb.bind(to: tableView.rx.items) { (tableView: UITableView, index: Int, element: String) -> UITableViewCell in

            guard let cell = tableView.dequeueReusableCell(withIdentifier: "NameCell") else { return UITableViewCell() }

            cell.textLabel?.text = element

            return cell

        }.disposed(by: bag)

    }

}







[ 2 ] tableView.rx.items(cellIdentifier:String) 사용하기 



    

private func bindTableView() {

        let cities = ["London", "Vienna", "Lisbon"]

        let citiesOb: Observable<[String]> = Observable.of(cities)

        

        citiesOb.bind(to: tableView.rx.items(cellIdentifier: "NameCell")) { (index: Int, element: String, cell: UITableViewCell) in

            cell.textLabel?.text = element

        }.disposed(by: bag)

    }



[ 3 ] tableView.rx.items(cellIdentifier: String, cellType: Cell.Type) 사용하기 


이런 식으로 NameCell 클래스를 만들어주고 



class NameCell: UITableViewCell {

    

}




    

private func bindTableView() {

        let cities = ["London", "Vienna", "Lisbon"]

        let citiesOb: Observable<[String]> = Observable.of(cities)


        citiesOb.bind(to: tableView.rx.items(cellIdentifier: "NameCell", cellType: NameCell.self)) { (index: Int, element: String, cell: NameCell) in

            cell.textLabel?.text = element

        }.disposed(by: bag)

    }





cell에 기본으로 있는 textLabel말고 프로퍼티를 만들어서 사용하여 보자 : ) 



class NameCell: UITableViewCell{

    @IBOutlet weak var numberLabel: UILabel!

    @IBOutlet weak var nameLabel: UILabel!

}




   

 private func bindTableView() {

        let cities = ["London", "Vienna", "Lisbon"]

        let citiesOb: Observable<[String]> = Observable.of(cities)


        citiesOb.bind(to: tableView.rx.items(cellIdentifier: "NameCell", cellType: NameCell.self)) { (index: Int, element: String, cell: NameCell) in

            cell.numberLabel.text = "\(index)"

            cell.nameLabel.text = element

        }.disposed(by: bag)

    }




[ 4 ] tableView.rx.items(dataSource:protocol<RxTableViewDataSourceType, UITableViewDataSource>) 사용하기 


tableView 를 어떻게 표현할지 미리 지정한 datasource를 사용한 방법이다 


pod으로 RxDataSource를 설치해주고 import 해준다 



import RxDataSources




그 후 이 클래스로 만든 datasource를 사용할 것이다 



open class RxTableViewSectionedReloadDataSource<S: SectionModelType>

    : TableViewSectionedDataSource<S>




RxDataSources에서는 SectionModelType을 따르는 SectionModel을 이미 구현해놓았는데, 이것을 사용하면 된다 



public struct SectionModel<Section, ItemType> {

    public var model: Section

    public var items: [Item]


    public init(model: Section, items: [Item]) {

        self.model = model

        self.items = items

    }

}


extension SectionModel

    : SectionModelType {

    public typealias Identity = Section

    public typealias Item = ItemType

    

    public var identity: Section {

        return model

    }

 }






   

 private func bindTableView() {

        let cities = ["London", "Vienna", "Lisbon"]

        

        let configureCell: (TableViewSectionedDataSource<SectionModel<String, String>>, UITableView,IndexPath, String) -> UITableViewCell = { (datasource, tableView, indexPath,  element) in

            guard let cell = tableView.dequeueReusableCell(withIdentifier: "NameCell", for: indexPath) as? NameCell else { return UITableViewCell() }

            cell.textLabel?.text = element

            return cell

        }

        

        let datasource = RxTableViewSectionedReloadDataSource<SectionModel<String, String>>.init(configureCell: configureCell)

        

        datasource.titleForHeaderInSection = { datasource, index in

            return datasource.sectionModels[index].model

        }

        

        let sections = [

            SectionModel<String, String>(model: "first section", items: cities),

            SectionModel<String, String>(model: "second section", items: cities)

        ]

        

        Observable.just(sections)

            .bind(to: tableView.rx.items(dataSource: datasource))

            .disposed(by: bag)

        

    }



이름이 너무 긴 것을 Typealias를 통해 줄여주고,

datasource 만드는 부분도 프로퍼티로 빼주고 코드를 정리하자 : ) 


   

    typealias CitySectionModel = SectionModel<String, String>

    typealias CityDataSource = RxTableViewSectionedReloadDataSource<CitySectionModel>

    

    private func bindTableView() {

        let firstCities = ["London", "Vienna", "Lisbon"]

        let secondCities = ["Paris", "Madrid", "Seoul"]

        

        let sections = [

            SectionModel<String, String>(model: "first section", items: firstCities),

            SectionModel<String, String>(model: "second section", items: secondCities)

        ]

        

        Observable.just(sections)

            .bind(to: tableView.rx.items(dataSource: cityDatasource))

            .disposed(by: bag)

    }

    

    private var cityDatasource: CityDataSource {

        let configureCell: (TableViewSectionedDataSource<CitySectionModel>, UITableView,IndexPath, String) -> UITableViewCell = { (datasource, tableView, indexPath,  element) in

            guard let cell = tableView.dequeueReusableCell(withIdentifier: "NameCell", for: indexPath) as? NameCell else { return UITableViewCell() }

            cell.textLabel?.text = element

            return cell

        }

        

        let datasource = CityDataSource.init(configureCell: configureCell)

        datasource.titleForHeaderInSection = { datasource, index in

            return datasource.sectionModels[index].model

        }

        

        return datasource

    }




이렇게 section을 가진 tableview를 만들어줄 수 있다 : ) 








https://github.com/RxSwiftCommunity/RxDataSources 

이 사이트를 가보면 Footer 넣기 등등 더 자세하게 알 수 있다 : ) 





******* 4가지 방법 비교 ************* 


[ 1 ] tableView.rx.items 

[ 2 ] tableView.rx.items(cellIdentifier:String) 

[ 3 ] tableView.rx.items(cellIdentifier: String, cellType: Cell.Type) 


- 3번만 CellType으로 지정해준 한가지 Type의 Cell만 사용할 수 있음 


ex) 1번 이런식으로 여러 cell 지정가능 


    viewModel.news.bind(to: tableView.rx.items) { (tableView, index, news) -> UITableViewCell in

    if news.type == "" {

    let cell = tableView.dequeueReusableCell(withIdentifier: "ShareTableViewCell") as? ShareTableViewCell

    return cell ?? UITableViewCell()

    } else {

    let cell = tableView.dequeueReusableCell(withIdentifier: "FeedTableViewCell") as? FeedTableViewCell

    return cell ?? UITableViewCell()

    }

    }.disposed(by: bag)



[ 4 ] tableView.rx.items(dataSource:protocol<RxTableViewDataSourceType, UITableViewDataSource>) 


- 4번만 Section 사용가능하다 




******* 꿀팁 *****************


.을 치고 .을 지우고 엔터를 누르면 클로저가 자동완성된다






이렇게 .을 지우고 엔터를 누르시오 







+ UITableViewDelegate 도 사용해보자 


viewdidload 에 



tableView.rx.setDelegate(self).disposed(by: bag)




를 추가해주고 



extension RxTableViewController: UITableViewDelegate {

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {

        return 100

    }

}



Delegate 안의 필요한 메소드를 활용하여준다 




옛날에 어떤 분께서  "Observable Event는 단방향이기 때문에 Delegate를 통해 값 가져오기가 불가능하다 이것이 rx를 이용한 tableView의 한계이다" 라고 말씀하셨는데 이 말을 이해하지는 못했다.


하지만 rx를 통해 tableView코드가 분산안되고 한눈에 들어오게 되는 점이 참 좋았는데 delegate는 rx쓰기 전과 동일한 방식으로 해줘야하는 것이 조~~~금 아쉬운 것 같다 🤔


반응형
댓글