khstar

RxSwift에서 UIPickerView 사용 및 데이터 변경하기 본문

개발/Swift

RxSwift에서 UIPickerView 사용 및 데이터 변경하기

khstar 2019. 6. 13. 15:54
반응형

최근에 RxSwift에 공부 중입니다.

개인적으로 좀 어렵네요. 완전히 새로 배우는 느낌?? 

 

우선 UIPickerView에 Observable을 Binding하는 방법은 쉽습니다. 

저도 잘 모르지만 RxSwift에 대해서는 조금은 알고 계신 분이 봐야 이해될 겁니다.

자세히 설명을 해드리고 싶어도 ㅜㅜ 

 

아래의 코드를 그대로 적용하시면 UIPicker가 생성은 됩니다.

//
//  ViewController.swift
//  RxswiftUIPickerView
//
//  Created by khstar on 13/06/2019.
//  Copyright © 2019 khstar. All rights reserved.
//

import UIKit
import RxSwift
import RxCocoa

class ViewController: UIViewController {

    @IBOutlet weak var pickerTextfield: UITextField!
    
    let pickerView = UIPickerView()
    //Picker의 데이터 입니다.
    var pickerData:[String] = ["1", "2", "3", "4", "5"]
    
    //PickerView의 ToolBar에 Done버튼 입니다.
    let doneToolBar = UIToolbar()
    let doneBarButton = UIBarButtonItem.init(barButtonSystemItem: UIBarButtonItem.SystemItem.done, target: nil, action: nil)
    
    let disposeBag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        setRx()
    }
    
    func setRx() {
        
        //DoneButton 눌려지는 경우 Action처리 입니다.
        doneBarButton.rx.tap.subscribe({
            _ in
            
            let i = self.pickerView.selectedRow(inComponent: 0)
            self.pickerTextfield.text = self.pickerData[i]
            self.pickerTextfield.resignFirstResponder()
            
        }).disposed(by: disposeBag)
        
        //여기서 pickerData를 Observable해서 PickerView에 Binding합니다.
        _ = Observable.just(pickerData).bind(to: pickerView.rx.itemTitles) {
            _, item in
            return "\(item)"
        }
        
        doneToolBar.sizeToFit()
        doneToolBar.items = [doneBarButton]
        
        pickerTextfield.inputAccessoryView = doneToolBar
        pickerTextfield.inputView = pickerView
        
    }
    
}

 

아래와 같은 화면이 생성됩니다. Textfield를 선택하면 Picker가 보이고 Done을 누르면 Textfield가 변경됩니다.

하지만 이글의 목적은 UIPickerView의 데이터를 변경하는 것입니다.

처음엔 그냥 Array데이터만 변경하면 되겠지 하는 생각을 했는데 아니었습니다. ㅜㅜ 

binding을 다시 해줘야 합니다.

아래는 변경된 코드입니다.

 

//
//  ViewController.swift
//  RxswiftUIPickerView
//
//  Created by khstar on 13/06/2019.
//  Copyright © 2019 khstar. All rights reserved.
//

import UIKit
import RxSwift
import RxCocoa

class ViewController: UIViewController {

    @IBOutlet weak var pickerTextfield: UITextField!
    @IBOutlet weak var numberBtn: UIButton!
    @IBOutlet weak var stringBtn: UIButton!
    
    let pickerView = UIPickerView()
    //Picker의 데이터 입니다.
    var pickerData:[String] = ["1", "2", "3", "4", "5"]
    
    //PickerView의 ToolBar에 Done버튼 입니다.
    let doneToolBar = UIToolbar()
    let doneBarButton = UIBarButtonItem.init(barButtonSystemItem: UIBarButtonItem.SystemItem.done,
                                             target: nil,
                                             action: nil)
    
    let disposeBag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        setRx()
    }
    
    func setRx() {
        
        //DoneButton 눌려지는 경우 Action처리 입니다.
        doneBarButton.rx.tap.subscribe({
            _ in
            
            let i = self.pickerView.selectedRow(inComponent: 0)
            self.pickerTextfield.text = self.pickerData[i]
            self.pickerTextfield.resignFirstResponder()
            
        }).disposed(by: disposeBag)
        
        //여기서 pickerData를 Observable해서 PickerView에 Binding합니다.
        setPickerBind()
        createPicker()
        
    }
    
    @IBAction func changeNumberData(_ sender: Any) {
        
        pickerData = ["1", "2", "3", "4", "5",]
        setPickerBind()
    }
    @IBAction func changeStringData(_ sender: Any) {
        
        pickerData = ["가", "나", "다", "라", "마",]
        setPickerBind()
    }
    
    func setPickerBind() {
        
        //pickerView의 delegate와 datasource를 nil 해주지 않으면 아래의 에러가 발생되면서 Crash됨
        //Assertion failed:
        //This is a feature to warn you that there is already a delegate (or data source)
        //set somewhere previously.
        
        pickerView.delegate = nil
        pickerView.dataSource = nil
        
        _ = Observable.just(pickerData).bind(to: pickerView.rx.itemTitles) {
            _, item in
            return "\(item)"
        }
    }
    
    func createPicker() {
        doneToolBar.sizeToFit()
        doneToolBar.items = [doneBarButton]
        
        pickerTextfield.inputAccessoryView = doneToolBar
        pickerTextfield.inputView = pickerView
    }
    
}

 

위에서 조심해야 되는 것은 pickerView의 delegate와 datasource를 nil 해주는 것입니다. 그냥 한 번만 생성해서 쭉~~ 사용할 때는 안 해줘도 되지만 binding을 다시 해줘야 할 때는 nil 해주지 않으면 아래와 같은 에러 메시지를 보여주면서 crash 됩니다.

 

Assertion failed: This is a feature to warn you that there is already a delegate (or data source) set somewhere previously.

 

참고로 이 문제는 delegate, datasource를 사용하는 경우 발생할 수 있는 듯합니다. tableview등에서도 발생하는 것 같더라고요.

 

그럼 이제 다시 실행을 해보면 버튼이 두 개 추가되었습니다.

1,2,3,4,5 버튼을 선택하면 Picker에 숫자문자가 보이고 가,나,다,라,마 버튼을 선택하시면 Picker에 문자가 보입니다.

친절하게 쓰고 싶은데 글 재주가 없어서 이렇게 뿐이 안되네요. ㅜㅜ 

 

Sample 소스입니다.

Git : https://github.com/khstar/Rxswift_UIPickerView_Sample

참고 : https://github.com/RxSwiftCommunity/RxDataSources/issues/185

반응형
Comments