본문 바로가기
iOS/RxSwift

[UIKit/RxSwift] UITableView를 RxSwift로 구현하는 방법

by 6cess 2024. 12. 2.

1.  데이터 바인딩

1) 기존 방식

기존의 방식으로 데이터를 바인딩하려면 UITableViewDataSource 프로토콜을 구현하고

cellForRowAt, numberOfRowsInSection 메서드를 작성

class NonRxTableViewController: UIViewController, UITableViewDataSource {
    private let tableView = UITableView()
    private let cardData: [(title: String, imageName: String)] = [
        (title: "Card 1", imageName: "sample1"),
        (title: "Card 2", imageName: "sample2"),
        (title: "Card 3", imageName: "sample3")
    ]

    override func viewDidLoad() {
        super.viewDidLoad()
        setupTableView()
    }

    private func setupTableView() {
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        tableView.dataSource = self
        view.addSubview(tableView)
        tableView.frame = view.bounds
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return cardData.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        let item = cardData[indexPath.row]
        cell.textLabel?.text = item.title
        cell.imageView?.image = UIImage(named: item.imageName)
        return cell
    }
}

 

2) Rx를 사용하는 방식

Observable을 통해 데이터를 UITableView에 바인딩

import RxSwift
import RxCocoa

class RxTableViewController: UIViewController {
    private let tableView = UITableView()
    private let disposeBag = DisposeBag()

    priavte let cardData: [(title: String, imageName: String)] = [
    (title: "Card 1", imageName: "sample1"),
    (title: "Card 2", imageName: "sample2")
]

    override func viewDidLoad() {
        super.viewDidLoad()
        setupTableView()
        bindTableView()
    }

    private func setupTableView() {
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        view.addSubview(tableView)
        tableView.frame = view.bounds
    }

    private func bindTableView() {
        Observable.just(cardData)
            .bind(to: tableView.rx.items(cellIdentifier: "cell")) { _, item, cell in
                cell.textLabel?.text = item.title
                cell.imageView?.image = UIImage(named: item.imageName)
            }
            .disposed(by: disposeBag)
    }
}

 

2. 클릭 이벤트 처리

1) 기존 방식

UITableViewDelegate를 구현하여 클릭 이벤트를 처리

// UITableViewDelegate 추가
class NonRxTableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
	
    // ... //
    
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.delegate = self
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let selectedItem = cardData[indexPath.row]
        print("Selected item: \(selectedItem.title)")
    }
}

 

2) Rx를 사용하는 방식

rx.itemSelected를 통해 클릭 이벤트를 처리

private func bindTableView() {

    Observable.just(cardData)
        .bind(to: tableView.rx.items(cellIdentifier: "cell")) { _, item, cell in
            cell.textLabel?.text = item.title
            cell.imageView?.image = UIImage(named: item.imageName)
        }
        .disposed(by: disposeBag)

    tableView.rx.itemSelected
        .subscribe(onNext: { [weak self] indexPath in
            guard let self = self else { return }
            let selectedItem = self.cardData[indexPath.row]
            print("Selected item: \(selectedItem.title)")
        })
        .disposed(by: disposeBag)
}

 

3. 기타 설정(Cell Height 설정)

1) 기존 방식

UITableViewDelegate을 구현하여 heightForRowAt 메서드를 사용

class NonRxTableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
	
    // ...
    
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.delegate = self
    }
    
    // ...

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

 

2) Rx를 사용하는 방식

CellHeight와 같이 UITableViewDelegate을 통해 직접 구현해야할 경우에 이미 TableView를 Rx로 구현하여 데이터 바인딩을 했다면 

반드시 아래와 같이 설정해야 한다.

RxCocoa는 내부적으로 UITableViewDelegateProxy라는 클래스를 사용하여 델리게이트를 대체하기 때문에 아래 설정을 통해 작성한 Delegate 메서드들이 인식되도록 적용시켜야 한다.

heightForRowAt, indexPath, didSelectRowAt indexPath 등등

extension RxTableViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 150
    }
}
private func bindTableView() {

    Observable.just(cardData)
        .bind(to: tableView.rx.items(cellIdentifier: "cell")) { _, item, cell in
            cell.textLabel?.text = item.title
            cell.imageView?.image = UIImage(named: item.imageName)
        }
        .disposed(by: disposeBag)

    tableView.rx.itemSelected
        .subscribe(onNext: { [weak self] indexPath in
            guard let self = self else { return }
            let selectedItem = self.cardData[indexPath.row]
            print("Selected item: \(selectedItem.title)")
        })
        .disposed(by: disposeBag)
        
    //delgate 설정 rx 시스템에 반영하기    
    tableView.rx.setDelegate(self).disposed(by: disposeBag)
}

'iOS > RxSwift' 카테고리의 다른 글

[RxSwift] Observable 항목 변환 메서드 정리  (0) 2024.12.11
[RxSwift] Observable 생성 관련 메서드들  (0) 2024.12.03
[RxSwift] AsyncSubject  (0) 2024.07.26