UITableViewをDynamic Prototypesで使っている場合はUIViewのendEditing(_:)メソッドを使ってキーボードを確実に閉じよう

2017.12.22

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

検証環境

本エントリは以下の環境で検証を行っています。

  • macOS Sierra バージョン 10.12.6
  • Xcode Version 9.2 (9C40b)
  • Swift 4
  • iPhone X 実機 iOS 11.2.1

キーボードタイプによってはリターンキーが表示されない

iOSのキーボードはそのタイプによってはリターンキーが表示されません。 例えば、キーボードタイプが.numberPadの場合リターンキーが表示されないため、自分で相当のボタンを実装する必要があります。
今回は以下のようにUITextFieldのinputAccessoryViewにUIToolbarを設定するケースを考えます。

private(set) var doneButtonInputAccessoryView: UIToolbar?

func setupDoneButtonInputAccessoryView() {
    doneButtonInputAccessoryView = UIToolbar(frame: CGRect(origin: .zero,
                                                           size: CGSize(width: 0,
                                                                        height: 44)))
    let spaceItem = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
    let doneItem = UIBarButtonItem(barButtonSystemItem: .done,
                                   target: self,
                                   action: #selector(didTapDoneButtonOnKeyboard(_:)))
    doneButtonInputAccessoryView?.items = [spaceItem, doneItem]
}

cell.textField.inputAccessoryView = doneButtonInputAccessoryView

見た目はこんな感じになります。キーボードの上にツールバーがあって右端に完了ボタンが配置されています。

キーボードを閉じる実装

完了ボタンをタップした時にはdidTapDoneButtonOnKeyboard(_:)メソッドが呼ばれるようにしているのでここでキーボードを閉じたいと思います。 キーボードを閉じる実装、以下のような実装をしていませんか?

tableView.visibleCells.forEach {
    let textFieldCell = $0 as? TextFieldCell
    textFieldCell?.textField.resignFirstResponder()
}

TextFieldCellはUITextFieldを持つUITableViewCellのカスタムサブクラスです。
tableView.visibleCellsでセルを取得してtextField.resignFirstResponder()を呼ぶことでキーボードを閉じています。

ただこのやり方はvisibleCellsを使っているのでセルが表示されている場合はキーボードがちゃんと閉じるのですが、スクロールして該当のセルが画面外となり表示されなくなった場合はキーボードは閉じてくれません。 これは、visibleCellsを使ってセルを取得しているため、表示されなくなったセルが取得できないためです。

cellForRow(at:)を使ってセルを取得すればいいのでは?

UITableViewには場所(IndexPath)を指定してセルを取得するメソッド、cellForRow(at:)があるので、これを使えばいいのでは?と思うかもしれません。
ですが、こちらもリファレンスAn object representing a cell of the table, or nil if the cell is not visible or indexPath is out of range. と記載されており、表示されていないセルについてはnilが返るので、visibleCellsと同様セルが画面外となり表示されなくなった場合はキーボードは閉じてくれません。

ではセルが表示されていない時でも確実にキーボードを閉じるにはどうすれば良いのでしょうか?

セルが表示されていない時でも確実にキーボードを閉じる

セルが表示されていない時でも確実にキーボードを閉じるには、UIViewのendEditing(_:)メソッドを使えばOKです。
このメソッドは該当のビューとそのサブビューのビュー階層を辿って、現在ファーストレスポンダーとなっているテキストフィールドを見つけて解除してくれます。 パラメーターのBool値は強制的にキーボードを閉じるかどうかの設定です。

今回のケースではテキストフィールドはUITableViewCellのサブビューなので、以下のようにUITableViewCellの親であるUITableViewのendEditing(_:)メソッドを呼べばキーボードを強制的に閉じることが可能です。

tableView.endEditing(true)

おわりに

UITableViewのセルの再利用サイクルが関連するので、もしかしたらハマっている人がいるかもしれないと思って記事にしてみました。 キーボードが閉じなくてお悩みの方に少しでもこの記事が役立てば幸いです。

参考