UITextFieldを空の状態からデリートキーを検出する

文字入力の検出

まずUITextFieldにおいて文字入力を検出するには主に2つの方法がある。
一つはUITextFieldDelegateを準拠し、textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Boolを用いる方法。もう一つはUIControlにあるaddTarget(_:action:for:)を以下のように書いて、テキストの内容が変更したタイミングで#selectorで指定した関数でハンドリングする。

let textField = UITextField()
textField.addTarget(self, action: #selector(textFieldDidChangeText(_:)), for: .editingChanged)

自分もいずれかの方法で文字入力のハンドリングをしていたが、ある時一つの問題を発見した。それは、テキストが空の状態からデリートキーの入力を検出できないことだ。
自分が直面したケースとしては、複数のテキストフィールドを用いてPINの入力する画面である。1桁入力するごとに次のテキストフィールドに移動し、デリートキーを入力すれば前のテキストフィールドに戻るといったものを実装しようとしたところ問題を見つけた。
今フォーカスされているテキストフィールドには文字がないので、デリートキーを検出できなかった。

f:id:culumn:20180223012201p:plain

f:id:culumn:20180223012156p:plain

テキストが空の状態からデリートキーを検出する

初めに紹介した2つの方法では無理なので調べていたら解決法を見つけた。UITextFieldにおいてデリートキーはUIKeyInputにあるdeleteBackward()を呼ぶことで入力しているらしい。つまりUITextFieldを継承したクラスを作成し、deleteBackward()をオーバライドして扱えるようにすればいい。そして、独自のDelegateを用意しそのタイミングで外のクラスに処理を委譲すればいい。具体的には以下のようなコードになる。

protocol CustomTextFieldDelegate: class {
  func didDeleteBackward(_ textField: CustomTextField)
}

class CustomTextField: UITextField {
  weak var deletionDelegate: CustomTextFieldDelegate?

  override func deleteBackward() {
    super.deleteBackward()
    deletionDelegate?.didDeleteBackward(self)
  }
}

また、これを使いframeworkにしたものサンプル付きでgithubに上げた。(簡単とは言え初めてframeworkを作った) github.com

参考