【Swift】キーボードと一緒にViewも上げる方法
TextFieldを編集する際にキーボードが下から出てきて入力欄を隠してしまう事があります。この記事ではキーボードの出現とともにView自体の位置をキーボードの高さ分上げる方法を書いていきます。
少しピンと来ないかもしれないので、完成図も一緒に添えておきます。
1.準備
Storyboardの準備です。(と言ってもUITextFieldが一つあるだけですが)
TextFieldに入力する際にキーボードが覆いかぶるように画面下部にUITextFieldを設置します。
2.キーボードや予測変換部分の出現を検知
キーボードの出現の検知にはUIResponderの「keyboardWillChangeFrameNotification」を使います。
他にもキーボードの出現を知らせてくれるUIResponderの一種で「keyboardWillShowNotification」というのがあります。
直感的にこちらの方が正しいように思えますが、「keyboardWillShowNotification」ですと予測変換部分が表示された際には反応しないので、TextFieldが予測変換部分に隠れる場合があります。
ViewControllerに以下を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import UIKit class ViewController: UIViewController { @IBOutlet weak var textField: UITextField! override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillChangeFrameNotification, object: nil) } @objc func keyboardWillShow(notification: NSNotification) { if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue { if self.view.frame.origin.y == 0 { self.view.frame.origin.y -= keyboardSize.height } else { let suggestionHeight = self.view.frame.origin.y + keyboardSize.height self.view.frame.origin.y -= suggestionHeight } } } } |
キーボードのframeに変化がある際にkeyboardWillShow function が呼ばれます。
さらには、if分岐で
viewのy座標が0(初期位置)の場合:
viewのy座標が初期位置では無い場合:
これはキーボードは既に表示されているが、予測変換部分がまだ表示されていないケースです。つまり、view.frame.origin.yは0からキーボードの高さ分引いた値になります。
let suggestionHeight = self.view.frame.origin.y + keyboardSize.height
self.view.frame.origin.y -= suggestionHeight
ですので、予測変換部分の高さは新しく検知されたキーボードの高さ足す
view.frame.origin.yになります。
あとは割り出した予測変換部分の高さをview.frame.origin.yから引いてviewの位置を調整します。
3.キーボードが隠れた際にViewの位置も戻す
入力が終わったらキーボードを閉まって、Viewも元に位置に戻す必要があります。
ViewControllerに以下を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 |
override func viewDidLoad() { super.viewDidLoad() self.textField.delegate = self NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillChangeFrameNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil) } @objc func keyboardWillHide() { if self.view.frame.origin.y != 0 { self.view.frame.origin.y = 0 } } |
1 2 3 4 5 6 7 8 |
extension ViewController: UITextFieldDelegate { func textFieldShouldReturn(_ textField: UITextField) -> Bool { self.view.endEditing(true) return false } } |
TextFieldのdelegateをViewControllerに渡して、「Return」が押された際に呼び出されるtextFieldShouldReturn functionから、TextFieldの編集を終了します。
これで「Return」が押されると同時にキーボードが隠れます。そしてUIResponderの「keyboardWillHideNotification」が呼び出されます。
keyboardWillHideNotificationを引き金として、keyboardWillHide functionが発火されます。
1 2 3 4 5 |
@objc func keyboardWillHide() { if self.view.frame.origin.y != 0 { self.view.frame.origin.y = 0 } } |
中身はシンプルでview.frame.origin.yが0(初期位置)じゃ無い場合は0に戻すようになっています。
最終的に、ViewControllerは以下のようになっています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
import UIKit class ViewController: UIViewController { @IBOutlet weak var textField: UITextField! override func viewDidLoad() { super.viewDidLoad() self.textField.delegate = self NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillChangeFrameNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil) } @objc func keyboardWillShow(notification: NSNotification) { if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue { if self.view.frame.origin.y == 0 { self.view.frame.origin.y -= keyboardSize.height } else { let suggestionHeight = self.view.frame.origin.y + keyboardSize.height self.view.frame.origin.y -= suggestionHeight } } } @objc func keyboardWillHide() { if self.view.frame.origin.y != 0 { self.view.frame.origin.y = 0 } } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillChangeFrameNotification, object: nil) NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil) } } extension ViewController: UITextFieldDelegate { func textFieldShouldReturn(_ textField: UITextField) -> Bool { self.view.endEditing(true) return false } } |
以上、キーボードと一緒にViewも上げる方法でした。
おまけ:タップでキーボードを隠す
画面のどこかをタップしたらキーボードが隠れるようにします。
ViewControllernい以下を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
override func viewDidLoad() { super.viewDidLoad() self.textField.delegate = self NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillChangeFrameNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil) let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard)) self.view.addGestureRecognizer(tapGesture) } @objc func dismissKeyboard() { self.view.endEditing(true) } |
最終的にViewControllerは以下のようになっています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
import UIKit class ViewController: UIViewController { @IBOutlet weak var textField: UITextField! override func viewDidLoad() { super.viewDidLoad() self.textField.delegate = self NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillChangeFrameNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil) let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard)) self.view.addGestureRecognizer(tapGesture) } @objc func keyboardWillShow(notification: NSNotification) { if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue { if self.view.frame.origin.y == 0 { self.view.frame.origin.y -= keyboardSize.height } else { let suggestionHeight = self.view.frame.origin.y + keyboardSize.height self.view.frame.origin.y -= suggestionHeight } } } @objc func keyboardWillHide() { if self.view.frame.origin.y != 0 { self.view.frame.origin.y = 0 } } @objc func dismissKeyboard() { self.view.endEditing(true) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillChangeFrameNotification, object: nil) NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil) } } extension ViewController: UITextFieldDelegate { func textFieldShouldReturn(_ textField: UITextField) -> Bool { self.view.endEditing(true) return false } } |
参考URL