RxCocoa의 UITextField.rx.text를 구독하면 리턴 키로 키보드가 닫힙니다.

소개



RxCocoa가 UITextField의 입력 값을 이벤트 스트림으로 받는 경우 UITextField.rx.text를 사용합니다.
import UIKit
import RxSwift
import RxCocoa

final class ViewController: UIViewController {

    @IBOutlet private weak var textField: UITextField!

    private let bag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()
        textField.rx.text.subscribe().disposed(by: bag)
    }
}

이와 같이 UITextField.rx.textsubscribe() 하면(자), 입력치를 이벤트 스트림로서 받아들일 뿐만 아니라, 왠지, 키보드의 리턴 키의 탭으로 키보드가 닫게 됩니다.



RxCocoa 뿐만 아니라 ReactiveCocoa 에서도 비슷한 거동이 됩니다.

ReactiveCocoa 에서는, 다음과 같이 해 UITextField 의 입력치를 이벤트 스트림로서 받을 수가 있어 역시, 키보드의 리턴 키의 탭으로 키보드가 닫게 됩니다.
import UIKit
import ReactiveSwift
import ReactiveCocoa

final class ViewController: UIViewController {

    @IBOutlet private weak var textField: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()
        textField.reactive.continuousTextValues.observe { _ in }
    }
}

왜 키보드가 닫히는가? 신경이 쓰였으므로, 조사해 보았습니다.

UITextField.rx.text 구현 개요



RxCocoa rx.text 구현를 따라 가면 다음과 같은 구현임을 알 수 있습니다.
  • UIControl.Events .allEditingEvents.valueChanged를 대상 작업에 추가
  • 작업이 전송되면 UITextField의 text 속성을 RxSwift의 이벤트 스트림으로 게시

  • 또한 ReactiveCocoa의 reactive.continuousTextValues ​​구현에서는 .allEditingEvents를 대상 작업에 추가하는 것 같습니다.

    리턴 키 입력으로 키보드가 닫히는 이유



    RxCocoa뿐만 아니라 .allEditingEvents를 대상 작업에 추가하면 리턴 키를 입력하면 키보드가 닫힙니다.

    다음과 같은 코드로 확인할 수 있습니다.
    import UIKit
    
    final class ViewController: UIViewController {
    
        @IBOutlet private weak var textField: UITextField!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // リターンキーの入力でキーボードが閉じるようになる
            textField.addTarget(self, action: #selector(self.hoo(sender:)), for: .allEditingEvents)
        }
    
        @objc func hoo(sender: Any) {
        }
    }
    
    .allEditingEvents에는 UITextField의 모든 편집 이벤트가 포함되어 있으므로 .editingDidEndOnExit도 포함됩니다.
    .editingDidEndOnExit를 대상 작업에 추가하면 반환 키를 입력하여 첫 번째 응답자를 종료할 수 있습니다. 이것이 본질적인 이유입니다.

    다음과 유사한 코드로 바꾸면 .editingDidEndOnExit 이벤트가 전송되지 않으므로 리턴 키를 누르면 키보드가 닫히지 않습니다.
    import UIKit
    
    final class ViewController: UIViewController {
    
        @IBOutlet private weak var textField: UITextField!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // リターンキーをタップしてもキーボードは閉じない
            var events = UIControl.Event.allEditingEvents
            events.subtract(.editingDidEndOnExit)
            textField.addTarget(self, action: #selector(self.hoo(sender:)), for: events)
        }
    
        @objc func hoo(sender: Any) {
        }
    }
    

    UITextField.rx.text에서 키보드가 닫히는 과정을 억제하려면



    키보드가 닫는 것은 편리하고, 통상은 이대로도 문제 없다고 생각합니다만, 다음과 같이 UITextFieldDelegate 에 적합하는 것으로, 이 거동을 억제할 수도 있습니다.
  • textFieldShouldReturn(_:)에서 false 반환
  • textFieldShouldEndEditing(_:)에서 false 반환
  • textFieldShouldReturn(_:)에서 false를 반환하면 .editingDidEndOnExit가 더 이상 전송되지 않으므로 리턴 키를 탭해도 UITextField.rx.text next 이벤트가 발생하지 않고 키보드를 닫는 것을 억제 할 수 있습니다. 합니다.
    final class ViewController: UIViewController, UITextFieldDelegate {
    
        @IBOutlet private weak var textField: UITextField!
    
        private let bag = DisposeBag()
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            textField.delegate = self
    
            textField.rx.text
                .bind(onNext: { print($0.debugDescription) })
                .disposed(by: bag)
        }
    
        func textFieldShouldReturn(_ textField: UITextField) -> Bool {
            // onNext は呼ばれない、キーボードも閉じない
            return false
        }
    }
    
    textFieldShouldEndEditing(_:).editingDidEndOnExit를 보낸 후에 호출되므로 리턴 키를 누르면 UITextField.rx.text next 이벤트가 발생하고 키보드가 닫히는 것을 억제 할 수 있습니다.
    final class ViewController: UIViewController, UITextFieldDelegate {
    
        @IBOutlet private weak var textField: UITextField!
    
        private let bag = DisposeBag()
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            textField.delegate = self
    
            textField.rx.text
                .bind(onNext: { print($0.debugDescription) })
                .disposed(by: bag)
        }
    
        func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
            // onNext は呼ばれるが、キーボードは閉じない
            return false
        }
    }
    

    그렇다고 하는 것으로, 의도에 맞추어 UITextFieldDelegate 를 구현하면 좋을 것 같습니다.
    RxCocoa가 아니라 UIKit의 사양에 의존한 내용이므로 ReactiveCocoa도 같은 제어가 가능합니다.

    요약


  • rx.text에 가입하면 리턴 키 탭으로 키보드가 닫힙니다
  • 이 동작은 대상 동작에 .editingDidEndOnExit를 추가 할 때 UIKit의 표준 동작입니다
  • .
  • 이 거동을 억제하고 싶은 경우는 UITextFieldDelegate 를 구현한다
  • 좋은 웹페이지 즐겨찾기