[RxSwift] Reactive TableView: Model Selection

Junyoung Park·2022년 8월 31일
0

RxSwift

목록 보기
2/25
post-thumbnail

RxSwift Beginners Episode 2 - Bind, Subscribe, onNext, Dispose.

Reactive TableView: Model Selection

구현 목표

  • 테이블 뷰를 구성하는 테이블 뷰 셀과 데이터 모델의 바인딩 가능
  • 바인딩 이후 특정 셀의 아이템/모델을 바탕으로 특정 이벤트를 구독 가능

구현 태스크

  1. RxSwift를 사용, 테이블 뷰의 셀과 주어진 데이터를 바인딩
  2. RxSwift를 사용하지 않고 UITableViewDelegate 함수를 사용해 didSelectRowAt를 커스터마이징한다.
  3. RxSwift를 사용해 UITableViewDelegate 함수를 사용하지 않고 선택 이벤트를 구독한다.
  4. 선택 이벤트를 감지했다면 다음 디테일 뷰로 모델의 값을 넘기고 네비게이션 스택에 푸쉬.

핵심 코드

    private func setTableViewModelSelectedRx() {
        tableView
            .rx
            .modelSelected(FoodModel.self)
            .subscribe(onNext: { foodModel in
                guard let navDetailVC = self.storyboard?.instantiateViewController(withIdentifier: "FoodViewController") as? FoodViewController else {
                    return
                }
                navDetailVC.foodImageName = foodModel.imageName
                self.navigationController?.pushViewController(navDetailVC, animated: true)
            })
            .disposed(by: disposeBag)
    }
  • rx를 통해 RxSwift 코드를 작성할 수 있고, 해당 테이블 뷰를 구성하는 모델의 선택 이벤트를 구독(subscribe) 가능
  • 구독한 뒤 네비게이션 푸쉬 이벤트를 발생시키기 위한 코드는 별도
  • disposed 또한 여타의 RxSwift 메소드 사용 방법과 같이 메모리 관리를 위한 cancellables에 넣기.

소스 코드

import UIKit
import RxSwift
import RxCocoa

class ViewController: UIViewController {
    let tableViewItems = [FoodModel(name: "Coke", imageName: "coke"), FoodModel(name: "Hamburger", imageName: "hamburger"), FoodModel(name: "Pizza", imageName: "pizza"), FoodModel(name: "Bulgogi", imageName: "bulgogi")]
    lazy var tableViewItemsRx = Observable.just(tableViewItems)
    let disposeBag = DisposeBag()

    @IBOutlet weak var tableView: UITableView!
    override func viewDidLoad() {
        super.viewDidLoad()
        title = "Menu"
        tableView.delegate = self
        setTableViewBindRx()
        setTableViewModelSelectedRx()
    }
    
    private func setTableViewBindRx() {
        tableViewItemsRx.bind(to: tableView
            .rx
            .items(cellIdentifier: "customTableViewCell", cellType: CustomTableViewCell.self)) {
                (tv, tableViewItem, cell) in
                cell.cellLabel.text = tableViewItem.name
                cell.cellImage.image = UIImage(named: tableViewItem.imageName)
            }
            .disposed(by: disposeBag)
    }
    
    private func setTableViewModelSelectedRx() {
        tableView
            .rx
            .modelSelected(FoodModel.self)
            .subscribe(onNext: { foodModel in
                guard let navDetailVC = self.storyboard?.instantiateViewController(withIdentifier: "FoodViewController") as? FoodViewController else {
                    return
                }
                navDetailVC.foodImageName = foodModel.imageName
                self.navigationController?.pushViewController(navDetailVC, animated: true)
            })
            .disposed(by: disposeBag)
    }
    
    private func setTableViewItemSelectedRx() {
        tableView
            .rx
            .itemSelected
            .subscribe(onNext: { indexPath in
                let foodModel = self.tableViewItems[indexPath.row]
                guard let navDetailVC = self.storyboard?.instantiateViewController(withIdentifier: "FoodViewController") as? FoodViewController else {
                    return
                }
                navDetailVC.foodImageName = foodModel.imageName
                self.navigationController?.pushViewController(navDetailVC, animated: true)
            })
            .disposed(by: disposeBag)
    }
}
  • RxSwift를 통해 테이블 뷰와 데이터 모델을 바인딩하는 setTableViewBindRx() 함수
  • RxSwift를 통해 테이블 뷰의 특정 클릭 이벤트를 구독, 다음 뷰로 네비게이션 이벤트를 발생시키는 함수 setTableViewModelSelectedRx() 함수
  • 특정 아이템 모델 자체를 선택할 수도 있고, 기존의 테이블 뷰 함수처럼 인덱스 패스를 통해 접근할 수도 있다.
extension ViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        guard let navDetailVC = storyboard?.instantiateViewController(withIdentifier: "FoodViewController") as? FoodViewController else {
            return
        }
        navDetailVC.foodImageName = tableViewItems[indexPath.row].imageName
        navigationController?.pushViewController(navDetailVC, animated: true)
    }
}
  • 기존의 델리게이트를 사용하고 있는 didSelectRowAt 함수

구현 화면

profile
JUST DO IT

0개의 댓글