はじめに
Swiftでは何もしなければ、UITextField
がキーボードで隠れてしまいます。
それを改善するために、UIScrollView
を使わずに画面をずらす方法を実装しました。
UITextField
がキーボードに隠れる場合のみ画面をずらすように実装しています。
これに関する情報や記事は多くあると思うのですが、ずらす幅の調整などで少し時間がかかってしまったので、記事として残しておきます。
コメントなどは、自分の分かりやすいように書いているので、細かい認識などは間違っている場合がありますが、ご容赦下さい。
1.キーボード開閉のタイミングを取得
まず、viewWillAppear
にNotification
を用いてキーボードが開閉するタイミングを取得する処理を記載。
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// キーボード開閉のタイミングを取得
let notification = NotificationCenter.default
notification.addObserver(self, selector: #selector(self.keyboardWillShow(_:)),
name: UIResponder.keyboardWillShowNotification,
object: nil)
notification.addObserver(self, selector: #selector(self.keyboardWillHide(_:)),
name: UIResponder.keyboardWillHideNotification,
object: nil)
}
2.キーボード表示通知の際の処理(keyboardWillShow)
@objc func keyboardWillShow(_ notification: Notification) {
// 編集中のtextFieldを取得
guard let textField = _activeTextField else { return }
// キーボード、画面全体、textFieldのsizeを取得
let rect = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
guard let keyboardHeight = rect?.size.height else { return }
let mainBoundsSize = UIScreen.main.bounds.size
let textFieldHeight = textField.frame.height
// ①
let textFieldPositionY = textField.frame.origin.y + textFieldHeight + 10.0
// ②
let keyboardPositionY = mainBoundsSize.height - keyboardHeight
// ③キーボードをずらす
if keyboardPositionY <= textFieldPositionY {
let duration: TimeInterval? =
notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double
UIView.animate(withDuration: duration!) {
// viewをy座標方向にtransformする
self.view.transform = CGAffineTransform(translationX: 0, y: keyboardTopPositionY - textFieldTopPositionY)
}
}
}
y座標の0は、上部であることを意識すると分かりやすいと思います。
① textField.frame.origin.y + textFieldHeight + 10.0
で、テキストフィールドの底辺より10.0下のy座標をtextFieldPositionY
として取得。(テキストフィールドとキーボードの間に隙間ができるように+10.0
としています。
② mainBoundsSize.height - keyboardHeight
で、キーボードの底辺のy座標をkeyboardPositionY
として取得。
③ ①が②より大きい(①が②の下にある)時に、viewをずらす。keyboardTopPositionY - textFieldTopPositionY
で、テキストフィールドが隠れている分だけずらす。
※ viewをキーボードの高さ分ずらしたい時は、-keyboardHeight
にします。
3.キーボード非表示通知の際の処理(keyboardWillHide)
@objc func keyboardWillHide(_ notification: Notification) {
let duration: TimeInterval? = notification.userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? Double
UIView.animate(withDuration: duration!) {
self.view.transform = CGAffineTransform.identity
}
}
その他、TextFieldに関する処理
// textFieldがタップされた際に呼ばれる
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
// 編集中のtextFieldを保持する
_activeTextField = textField
return true
}
// リターンがタップされた時にキーボードを閉じる
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
// 画面をタップした時にキーボードを閉じる
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
コード全体
以下、コードの全文です。
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
@IBOutlet private var textField: UITextField!
// 編集中のtextFieldを保持する変数
private var _activeTextField: UITextField? = nil
override func viewDidLoad() {
super.viewDidLoad()
textField.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// キーボード開閉のタイミングを取得
let notification = NotificationCenter.default
notification.addObserver(self, selector: #selector(self.keyboardWillShow(_:)),
name: UIResponder.keyboardWillShowNotification,
object: nil)
notification.addObserver(self, selector: #selector(self.keyboardWillHide(_:)),
name: UIResponder.keyboardWillHideNotification,
object: nil)
}
// キーボード表示通知の際の処理
@objc func keyboardWillShow(_ notification: Notification) {
// 編集中のtextFieldを取得
guard let textField = _activeTextField else { return }
// キーボード、画面全体、textFieldのsizeを取得
let rect = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
guard let keyboardHeight = rect?.size.height else { return }
let mainBoundsSize = UIScreen.main.bounds.size
let textFieldHeight = textField.frame.height
let textFieldPositionY = textField.frame.origin.y + textFieldHeight + 10.0
let keyboardPositionY = mainBoundsSize.height - keyboardHeight
if keyboardPositionY <= textFieldPositionY {
let duration: TimeInterval? =
notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double
UIView.animate(withDuration: duration!) {
self.view.transform = CGAffineTransform(translationX: 0, y: keyboardTopPositionY - textFieldTopPositionY)
}
}
}
// キーボード非表示通知の際の処理
@objc func keyboardWillHide(_ notification: Notification) {
let duration: TimeInterval? = notification.userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? Double
UIView.animate(withDuration: duration!) {
self.view.transform = CGAffineTransform.identity
}
}
// textFieldがタップされた際に呼ばれる
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
// 編集中のtextFieldを保持する
_activeTextField = textField
return true
}
// リターンがタップされた時にキーボードを閉じる
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
// 画面をタップした時にキーボードを閉じる
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
}
参考URL