RxSwift 정리 6 - UI (TableView .bind .drive .orEmpty)

2021. 8. 24. 16:35Developer.TokkiSea/Apple

반응형

 

 

TableView.rx

기존 방식이라면 UIViewController에 TableView 하나 추가

delegate와 dataSource를 UIViewController에 연결하고

UITableViewDelegate, UITableViewDataSource 를

상속한 다음

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)

를 오버라이딩 한후

UITableViewCell 추가하고 클래스를 만들어 연결하고..

과정을 생략할 수 있습니다.

그냥 UITableView 추가하고 IBOutlet만 연결한 후

아래 코드를 작성하면 5개의 Cell 이 생성됩니다.

.bind()

내부적으로 subscribe를 사용하고 onNext 만 처리합니다.

Observable<[...]> 만 입력 가능해 보입니다.

@IBOutlet var tableView: UITableView!
var disposeBag = DisposeBag()

func bindTableView() {
    let alphaBet = Observable.of(["A", "B", "C", "D", "E"])
    alphaBet.bind(to: tableView.rx.items) { (tableView: UITableView, index: Int, element: String) in
        let cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
        cell.textLabel?.text = element
        return cell
    }.disposed(by: disposeBag)
}
// A, B, C, D, E 가 적혀있는 Cell 이 생성된다.

// 다중 셀
enum MyModel {
    case textEntry(String)
    case pairOfText(String, String)
}
func bindTableView() {
    let observable = Observable<[MyModel]>.just([
        .textEntry("A"),
        .pairOfText("B", "C"),
        .textEntry("D"),
        .pairOfText("E","F")
        ])
    observable.bind(to: tableView.rx.items) { (tableView: UITableView, index: Int, element: MyModel) in
        let indexPath = IndexPath(item: index, section: 0)
        switch element {
            case .textEntry(let txt1):
                let cell = tableView.dequeueReusableCell(withIdentifier: "oneTextCell", for: indexPath) as! oneTextCell
                cell.one.text = txt1
                return cell
            case .pairOfText(let txt2, let txt3):
                let cell = tableView.dequeueReusableCell(withIdentifier: "twoTextCell", for: indexPath) as! twoTextCell
                cell.two.text = txt2
                cell.three.text = txt3
                return cell
          }
    }.disposed(by: disposeBag)
}

 

TableView.rx.modelSelected

셀을 선택

tableView.rx.modelSelected(String.self)
.subscribe(onNext: { model in
    print("\(model) was selected")
})
.disposed(by: disposeBag)
// 선택한 알파벳 String

modelSelected(_:), modelDeselected(_:), itemSelected, itemDeselected 

itemAccessoryButtonTapped 

itemInserted, itemDeleted, itemMove (셀 editMode에서) 

willDisplayCell, didEndDisplayingCell.....

등 기존에 사용하던 UITableViewDelegate 함수들 이름 사용.

 

.drive()

subscribe의 UI(Main Thread)용

기존 subscribe로 OnSubscribe(Thread...) 식으로 사용해도 무관합니다.

let usernameValid = textField1.rx.text.asDriver().map { $0!.utf8.count > 0 }
let passwordValid = textField2.rx.text.asDriver().map { $0!.utf8.count > 0 }
let credentialsValid: Driver<Bool> =
    Driver.combineLatest(usernameValid, passwordValid) { $0 && $1 }
credentialsValid.drive(onNext: { [weak self] valid in
    print("ok enable login Button")
})
.disposed(by: disposeBag)

 

.orEmpty

위의 코드의 첫 번째 두 번째를 보면 $0에! 표가 붙어 있습니다.

map으로 들어오는 값이 optional이라서 그렇다.

nil이라도 들어오면 크래시가 납니다.

.orEmpty를 붙여주면 optional이 제거되어서 !를 제외해도 되고

nil 이면 "" 이 리턴됩니다.(이 코드에서는 그럴리는 없지만!!)

let usernameValid = textField1.rx.text.orEmpty.asDriver().map { $0.utf8.count > 0 }
let passwordValid = textField2.rx.text.orEmpty.asDriver().map { $0.utf8.count > 0 }
let credentialsValid: Driver<Bool> =
    Driver.combineLatest(usernameValid, passwordValid) { $0 && $1 }
credentialsValid.drive(onNext: { [weak self] valid in
    print("ok enable login Button")
})
.disposed(by: disposeBag)

 

 

 

 

 

 

 

반응형