UITextFieldに文字数制限をつける
やりたいこと
- UITextFieldに入力できる文字数を制限する
- 変換中は制限されない
- 確定した時にオーバーしている分は除かれる
- 確定した時に文字数をカウントする
考えられる解決策
stringByReplacingCharactersInRangeを使う
textField.delegate = self
として、以下を追加。
extension ViewController: UITextFieldDelegate { func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { // 文字数制限の処理 return true } }
英語入力の場合は問題ないが、
日本語などの変換がある言語の場合はうまく動かなかった。
関数が実行されるタイミングと変換との相性が悪いようだ。
UIControlEvents.EditingChanged
UIControlEventsというもので、様々なイベントをキャッチできるみたい。
その中に、textFieldChanged
なるイベントがあった。
これを感知した時のactionを設定すれば良い。
重要なのはChangedと過去形なところ。
textField.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged)
markedTextRange
は、変換中(確定前)の文字列である。
今回の私の要求に応えてくれるめっちゃ便利なやつ。
@objc private func textFieldDidChange() { guard let text = textField.text else { return } // 変換中はスルー if textField.markedTextRange != nil { return } if text.count > maxLength { textField.text = String(text.prefix(maxLength)) } }
UITextFieldTextDidChange
viewDidLoad
等でUITextFieldTextDidChange
通知を受け取れるように設定。
こっちのやり方でも希望の動作は得られる。
ただ、同一クラス内でNotification使うのはどうなんかな、、
NotificationCenter.default.addObserver( self, selector: #selector(textFieldDidChange), name: NSNotification.Name.UITextFieldTextDidChange, object: nil )
呼ばれる関数の中身はさっきと同じ。
@objc private func textFieldDidChange(notification: NSNotification) { guard let text = textField.text else { return } if textField.markedTextRange != nil { return } if text.count > maxLength { textField.text = String(text.prefix(maxLength)) } }
ベストな解決策
2つ目のUIControlEvents.EditingChanged
を使うのが個人的にベスト。
別ビューに文字列を渡したい時はNotificationを使うのがよそそう。