Slack や LINE のようにスクロールに合わせてインタラクティブにキーボードを引っ込める

こんにちは! コインチェックで iOS アプリエンジニアをしている髙橋です。 今回、 Coincheck アプリのチャット画面のネイティブ化を行いました。その際に Slack や LINE のようにドラッグでキーボードを引っ込められたらもっとよくなるのでは?とチームから提案があったので実装を行いました。実装時に Web で調べてみても日本語での情報が意外となかったため、記事にしました。

こちらにサンプルリポジトリがあるので合わせてご覧ください。

github.com

前提

まずは以下の GIF の状態から開始です。一般的なメッセージアプリやサンプルでよくみられるように、キーボードの表示/非表示に合わせてメッセージの入力フィールドが上下に動くような状態です。

ここから TableView のスクロールに合わせてキーボードとメッセージの入力フィールドを引っ込めるには以下の対応を行います。

  1. キーボードをスクロールに合わせて引っ込める
  2. UIPanGestureRecognizer を利用し、入力フィールドをキーボードに合わせて移動させる

順番に対応を見ていきます。

1. キーボードをスクロールに合わせて引っ込める

こちらはとても簡単で UITableView に生えている keyboardDismissMode の設定を以下のように設定してあげます。

tableView.keyboardDismissMode = .interactive

こうしてあげることで、 TableView を下方向にドラッグした際に、ドラッグに合わせていい感じにキーボードを引っ込めてくれます。ただこの設定で引っ込めてくれるのはあくまでキーボードだけなので、入力フィールドは開発者がキーボードに合わせて移動させる処理を書く必要があります。つらみ。

2.入力フィールドを移動させる

UIPanGestureRecognizer の実装

PanGesture の実装は次の通りで、changed の状態の時に、指の現在位置を計算します。 キーボードの高さをプロパティとして持っておき、指の位置がキーボードの上端に触れたタイミングでキーボードが引っ込み始めるので、合わせて入力フィールドも引っ込めていきます。

@IBAction private func panGestureAction(_ gestureRecognizer: UIPanGestureRecognizer) {
    if case .changed = gestureRecognizer.state {
        let origin = gestureRecognizer.location(in: view)
        // 現在の指の位置
        let height = UIScreen.main.bounds.height - origin.y

        // 指がキーボードの上端にとどいたら、入力フィールドも移動させる
        if height <= self.currentKeyboardHeight {
            self.containerViewBottomConstraint.constant = -(height - self.bottomSareAreaHeight)
        }
    }
}

UIPanGestureRecognizer の制御

このままでは TableView の PanGesture が優先されてしまい、先ほど実装した PanGesture が効きません。そのため、 UIGestureRecognizerDelegate を利用してどの Gesture を有効/無効にするかの制御を行う必要があります。
今回は少々雑ですが、shouldRecognizeSimultaneouslyWith メソッドを利用し、TableView のドラッグジェスチャーと同時に認識されるようにすることで対応しました。

extension ChatViewController: UIGestureRecognizerDelegate {
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        panGestureRecognizer == gestureRecognizer
    }
}

これでキーボードに合わせて入力フォームも引っ込むようになりました 🙌

完成!🙌

まとめ

ジェスチャーで引っ込むキーボードと入力フォームの実装を行なったのは初めてだったのですが、意外と検索してもまとまった情報が出てこなかったので記事にしてみました!
UI ライブラリの実装を真似するなどしたのですがうまくいかず、自分がイメージしていた方法を一から実装したことで期待通りに動作し、結果として遠回りになるなど少々苦労しました。
今のご時世なので SwiftUI を使った実装も多いかと思いますが、まだ UIKit でないと・・・というアプリもやはり多いのかなと思いますので少しでも参考になれば幸いです!