본문 바로가기
iOS/Swift

RxSwift UITextField

by DnaJ 2019. 6. 1.
반응형

RxSwift UITextField 

UITextField에 관한 포스팅이다. 많이 사용한는 컨트롤중에 하나이다.

editingChanged에서 변경된 text를 처리하는 로직을 많이 구현했었다. (본인...)

UITextField에서 가장 많이 처리하는 곳은 로그인 화면이다.

id와 password를 입력하는데 꼭 필요한 컨트롤이 UITextField이니 말이다.

그 외에 간단한 입력을 받기 위해 사용해야 하는 컨트롤인데 사용해 본 코드만 소개하려 한다. 

 

이미지와 같은 코드다. 

@IBOutlet var textField: UITextField!
    let disposeBag = DisposeBag()

    func initTextField() {
        
        // editingChanged 이벤트가 발생 했을 때
        self.textField.rx.controlEvent([.editingChanged])
            .asObservable()
            .subscribe(onNext: { _ in
               print("editingChanged : \(self.textField.text ?? "")")
            }).disposed(by: disposeBag)
        
        // textField.rx.text의 변경이 있을 때
        self.textField.rx.text
            .subscribe(onNext: { newValue in
             print("rx.text subscribe : \(newValue ?? "")")
        }).disposed(by: disposeBag)
        
        // textField.text의 변경이 있을 때
        self.textField.rx.observe(String.self, "text")
            .subscribe(onNext: { newValue in
            print("observe text : \(newValue ?? "")")
        }).disposed(by: disposeBag)
        
    }

 

controlEvent

controlEvent를 살펴보면 배열로 .editingChanged가 들어가있다.

배열로 들어갔기 때문에 하기 코드와 같이 여러 이벤트를 한번에 구독할 수 있다.

editingChanged이벤트가 발생했을 때 onNext에서 아무 데이터도 들어오지 않는다. 

 

self.textField.rx.controlEvent([.editingChanged, .editingDidEnd])
            .asObservable()
            .subscribe(onNext: { _ in
               print("editingChanged : \(self.textField.text ?? "")")
            }).disposed(by: disposeBag)

 

 

텍스트 변경 후 처리

 

텍스트 변경 감지하기

텍스트가 변경이 되었을 때 우리는 어떠한 처리를 해야 할 경우가 있다.

가령 이메일형식이 맞는지, 사용불가능한 문자가 들어가 있는지 말이다.

그러기 위해 텍스트가 변경이 되었을 때 개발자가 알아야 한다.

 

먼저 아래코드를 보면 textFiled.rx.text구독하고 있다.

우리가 키패드로 입력을 하게 되면 newValue에서 textFiled의 text가 들어오게 된다.

키패드로 입력을 할 때마다 이벤트가 들어온다.

textField.text = "new"와같이 대입을 했을 때는 이벤트가 발생하지 않는다.

textField.tx.text 는 textField의 property가 아닌 extension에 있는 ControlProperty이기 때문이다.

아래 콜스택은 키패드로 입력했을 때 print를 찍기 전까지 나오는 콜스택이다.

ControlTarget이벤트 핸들러 에서부터 발생하고 있다. 즉, ControlEvent부터 이벤트가 시작된다고 보면 될것이다.

굳이 콜스택 까지 볼 필요는 없지만 다음 설명할 코드 때문에 콜스택을 봤다.

self.textField.rx.text 구독

 

하기 코드를 보면 textFieldpropertytext를 구독하고 있다.

단어만 적어보면 위의 코드와 무슨 차이인지 구분하기 힘들다.

하지만 동작과 콜스택을 보면 명확하게 차이를 알 수 있다.

하기 코드는 정확하게 KOV를 이용하여 구독한 것이다.

KOV를 사용했을 때는 키패드로 textField에 입력을 했을 때 이벤트가 발생하지 않는다.

하지만 textField.text = "new"와 같이 대입을 했을 때 이벤트가 발생한다.

 

self.textField.rx.observe(String.self, "text") 구독 

 

변경된 스트링 property에 저장하기

스트링을 property로 가지고 있어야 하는 경우가 발생한다. 없어도 있다고 생각하자....ㅎㅎ

하기 코드는 property변경된 스트링을 저장하기 위한 코드다.

strProperty라는 BehaviorRelay를 만들어 구독을 하고 있다.

텍스트를 변경될때 마다 strProperty에 값이 변경되어 print가 된다.

 이것도 역시 textFeild.rx.text로 시작하기 때문에 property에 직접 대입하면 이벤트가 발생하지 않는다는점 주의하자.

 

 let disposeBag = DisposeBag()
    let strProperty =  BehaviorRelay(value: "")
    
    func insertProperty() {
        
        textField.rx.text.orEmpty
            .map { $0 as String }
            .bind(to: self.strProperty)
            .disposed(by: disposeBag)
        
        self.strProperty.subscribe( onNext: { newValue in
            print("property changed : \(newValue) ")
        }).disposed(by: disposeBag)
    }

 

orEmpty

ControlPropertyType의 Extention 에 있는 함수다.

ControlPropertygeneric TypeString으로 정의를 해놓은 것이다.

orEmpty를 사용하지 않고 textField.rx.text.map { } 이렇게 사용해도 무관하다.

하지만, map안에서 캐스팅을 해주어야 하기 때문에 orEmpty를 사용하는 것이다.

 

extension ControlPropertyType where Element == String? {
    /// Transforms control property of type `String?` into control property of type `String`.
    public var orEmpty: ControlProperty<String> {
        let original: ControlProperty<String?> = self.asControlProperty()

        let values: Observable<String> = original._values.map { $0 ?? "" }
        let valueSink: AnyObserver<String> = original._valueSink.mapObserver { $0 }
        return ControlProperty<String>(values: values, valueSink: valueSink)
    }
}

 

 

bind

bind : 묶다, 연결시키다.

bind를 사용해 map에서 나오는 결과를 strProperty에 bind한다.

 

property 구독은 아래 글을 참고하기 바란다.

2019/05/25 - [iOS/Swift] - RxSwift property observe (BehaviorRelay, Variable)

 

RxSwift property observe (BehaviorRelay, Variable)

RxSwift property observe (BehaviorRelay, Variable) property 구독하기 property를 구독하기 위하여 BehaviorRelay를 사용한다. RxSwift 4.0 이전에는 Variable을 사용했었다. 하지만 Variable은 Depreated가 예..

myseong.tistory.com

 

응용하기

위와 같은 방법들은 텍스트가 변경될 때마다 이벤트가 발생한다.

비효율 적이다. 분명 위와 같은 방법으로 해야 하는 경우가 분명 있겠지만

하기와 같은 방법으로 filtermap을 응용하여 특정한 경우이벤트를 발생 시키는 방법도 있다.

 

 func filerTextFiled() {
 		// 첫번째 filter에 $0을 사용한경우
        self.textField.rx.text.orEmpty
            .filter { $0 == "222" }
            .map { $0 as String }
            .subscribe(onNext: { newValue in
                print(".filter map as String : \(newValue) ")
            }).disposed(by: disposeBag)
        
        // 두번째 filter에 value에 값을 넣어 사용한경우
        self.textField.rx.text.orEmpty
            .filter { value in
                if value == "222" {
                    return true
                } else {
                    return false
                }
            }
            .map { $0 as String }
            .subscribe(onNext: { newValue in
                print(".filter map as String : \(newValue) ")
            }).disposed(by: disposeBag)
    }

 

같은 로직을 두가지 방법으로 사용했다.

위의 로직들은 textField의 입력된 값이 "222" 으로 변경 되었을 때 "222"를 print해주는 코드다.

첫번째는 filter에 $0을 사용하여 비교한 것이고 두번째는 filter에 $0을 value로 바꿔서 사용한 경우다.

$0을 value로 받아서 처리했을 경우 조금더 복잡한 계산을 할 수 있다는 것을 보여주기 위하여

같은 동작을 하는 로직라도 샘플로 준비를 했다.

반응형

댓글