1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【iOS】ScrollView上で、UITextFieldのフォーカスを切り替えた際にUITextFieldが隠れないようにする

Posted at

嵌ったのでメモしておきます。。

概要

ScrollView上に複数のUITextFieldが縦に並んでいる画面で、キーボードを表示したままフォーカスしているUITextFieldを切り替える

ScrollViewに触れずに、UITextFieldに対するフォーカスを切り替えられるタイプもスコープ。(キーボード上に戻るボタンと進むボタンが乗ってる、↓のようなToolbarがついててそれでフォーカスを切り替えるイメージ)

toolbar.png

その際に、フォーカスしているUITextFieldが隠れないようにしたい(当然単にキーボード表示をする際にも隠れて欲しくない)

対応内容

キーボードが表示/非表示になる際に、以下のNotificationが飛ぶので、その中でscrollViewのinsetを切り替える事で対応できました。

  • keyboardWillShowNotification
  • keyboardWillHideNotification

実装

override func viewDidLoad() {
    super.viewDidLoad()

    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShown(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHidden(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}

@objc private func keyboardWillShown(_ notification: Notification?) {
    guard let userInfo = notification.userInfo as? [String: Any],
        let keyboardInfo = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else {
        return
    }
    
    // Keyboardの高さ分、scrollViewのbottomに対してinsetsを指定する
    let keyboardSize = keyboardInfo.cgRectValue.size
    let scrollInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height: right: 0)
    scrollView.contentInset = scrollInsets
    scrollView.scrollIndicatorInsets = scrollInsets
}

@objc private func keyboardWillHidden(_ notification: Notification?) {
    // insetsを元に戻す
    scrollView.contentInset = .zero
    scrollView.scrollIndicatorInsets = .zero
}

keyboardWillShowNotification は名称的にキーボードが表示される時に通知されるように見えますが、
実際はキーボードを表示したまま別のUITextFieldにフォーカスした際も通知されるようです。

そのため、UITextFieldのフォーカスを変更した際に、キーボードのタイプが異なるためにキーボードの高さが変わってしまった場合も再度その高さを取得、Insetsを設定することが可能なため、上記を実装しておけば

  • キーボードが表示されるタイミング
  • キーボードを表示したまま、別のUITextFieldにフォーカスを変更
    • その結果キーボードの高さが変わる場合も含む

の両方に対して対応できるようでした。

嵌った点

最初、keyboardInfoを keyboardFrameBeginUserInfoKey から取っていたんですが、
これだとどうもキーボードを表示したままフォーカスを変更した際に、正しくキーボードの高さを取れないようで
フォーカスしているUITextFieldがキーボードに隠れてしまうことがありました。

https://developer.apple.com/documentation/uikit/uikeyboardframebeginuserinfokey
https://developer.apple.com/documentation/uikit/uikeyboardframeenduserinfokey

beginはおそらくキーボードの表示前にキーボードの高さを取っており、
そのためフォーカス変更前のキーボードの高さを取ってしまっていたため、正しくInsetsが更新できていなかったのかなーと思っております😥
キーボードの高さが変わる可能性がある場合はもちろん、そうでない場合もendで取得する方が安全そうですね。。

まとめ

https://developer.apple.com/library/archive/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html
この手法を見つける前にも色々と試したのですが、どれも今ひとつ決まらず、最終的に上記のApple公式ドキュメントのおかげで解決したという...orz
迷ったらまずは公式ドキュメントを当たってみる癖が大事ですね😓

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?