Last updated on: September 3, 2022
In today’s tutorial, we will learn how to make a view animate when the keyboard appears or disappears.
It can be any kind of view: a UITextField
, a UIButton
; it doesn’t matter.
Contents
Creating the UI
In this example, we have a login screen with a UITextField
for the username and password.
At the bottom, we have a UIButton
that we want to move up/down when the keyboard appears/disappears.
To do that, we set an IBOutlet
of the bottom constraint of this UIButton
We select the bottom constraint in our storyboard and then we ctrl + drag n drop that constraint over to our Swift file and give it the name loginButtonBottomConstraint and press Connect
Making View move with the Keyboard
To detect when the keyboard starts to appear or disappear, we add two notifications to our view.
class ViewController: UIViewController {
@IBOutlet var usernameTextField: UITextField!
@IBOutlet var passwordTextField: UITextField!
@IBOutlet var loginButton: UIButton!
@IBOutlet var loginButtonBottomConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
// Notifications for when the keyboard opens/closes
NotificationCenter.default.addObserver(
self,
selector: #selector(self.keyboardWillShow),
name: UIResponder.keyboardWillShowNotification,
object: nil)
NotificationCenter.default.addObserver(
self,
selector: #selector(self.keyboardWillHide),
name: UIResponder.keyboardWillHideNotification,
object: nil)
}
@objc func keyboardWillShow(_ notification: NSNotification) {
// Add code later...
}
@objc func keyboardWillHide(_ notification: NSNotification) {
// Add code later...
}
}
Code language: Swift (swift)
We then create a new method called moveViewWithKeyboard, which takes a NSNotification
parameter and the bottom constraint of the view we want to change. We also pass in a Boolean
value to know if we have to move the view up or down.
func moveViewWithKeyboard(notification: NSNotification, viewBottomConstraint: NSLayoutConstraint, keyboardWillShow: Bool) {
// Add code later...
}
Code language: Swift (swift)
Inside this method, we get the keyboard height so that we can move the view above the keyboard when it appears.
func moveViewWithKeyboard(notification: NSNotification, viewBottomConstraint: NSLayoutConstraint, keyboardWillShow: Bool) {
// Keyboard's size
guard let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else { return }
let keyboardHeight = keyboardSize.height
// ...
}
Code language: Swift (swift)
Next, we get the keyboard’s duration and curve animation.
This will make the view move the same way as the keyboard, creating a nice smooth synchronized animation.
func moveViewWithKeyboard(notification: NSNotification, viewBottomConstraint: NSLayoutConstraint, keyboardWillShow: Bool) {
// ...
// Keyboard's animation duration
let keyboardDuration = notification.userInfo![UIResponder.keyboardAnimationDurationUserInfoKey] as! Double
// Keyboard's animation curve
let keyboardCurve = UIView.AnimationCurve(rawValue: notification.userInfo![UIResponder.keyboardAnimationCurveUserInfoKey] as! Int)!
// ...
}
Code language: Swift (swift)
Then, we set the bottom constraint. If the keyboard is going to appear, we use the keyboard height + 20 (the bottom constraint). When the keyboard is going to disappears, we set the constraint back to 20.
We also check if the safe area exists. If the safeAresInsets
bottom equals 0, the device doesn’t have safe areas (devices without the notch, like iPhone 8).
This allows us to set the correct bottom constraint of the view we’re moving.
func moveViewWithKeyboard(notification: NSNotification, viewBottomConstraint: NSLayoutConstraint, keyboardWillShow: Bool) {
// ...
// Change the constant
if keyboardWillShow {
let safeAreaExists = (self.view?.window?.safeAreaInsets.bottom != 0) // Check if safe area exists
let bottomConstant: CGFloat = 20
viewBottomConstraint.constant = keyboardHeight + (safeAreaExists ? 0 : bottomConstant)
}else {
viewBottomConstraint.constant = 20
}
// ...
}
Code language: Swift (swift)
Finally, using the duration and the curve of the keyboard, we create a UIViewPropertyAnimator
to update the view’s bottom constraint with animation.
func moveViewWithKeyboard(notification: NSNotification, viewBottomConstraint: NSLayoutConstraint, keyboardWillShow: Bool) {
// ...
// Animate the view the same way the keyboard animates
let animator = UIViewPropertyAnimator(duration: keyboardDuration, curve: keyboardCurve) { [weak self] in
// Update Constraints
self?.view.layoutIfNeeded()
}
// Perform the animation
animator.startAnimation()
}
Code language: Swift (swift)
Now, we go back to the keyboardWillShow method we created before, and we call the moveViewWithKeyboard method only when the usernameTextField or passwordTextField is edited.
This prevents moving the view when another view sits on top of this UIViewController
.
For instance, if you have a “Lost your password?” screen that shows a popup window with a text field for users to enter their email address to reset their password, you don’t want to see the “Login” button move when the keyboard appears/disappears for the email text field.
@objc func keyboardWillShow(_ notification: NSNotification) {
// Move the view only when the usernameTextField or the passwordTextField are being edited
if usernameTextField.isEditing || passwordTextField.isEditing {
moveViewWithKeyboard(notification: notification, viewBottomConstraint: self.loginButtonBottomConstraint, keyboardWillShow: true)
}
}
Code language: Swift (swift)
We do the same in the keyboardWillHide and we set the parameter keyboardWillShow to false
.
@objc func keyboardWillHide(_ notification: NSNotification) {
moveViewWithKeyboard(notification: notification, viewBottomConstraint: self.loginButtonBottomConstraint, keyboardWillShow: false)
}
Code language: Swift (swift)
So, in the end, will look like this:
class ViewController: UIViewController {
@IBOutlet var usernameTextField: UITextField!
@IBOutlet var passwordTextField: UITextField!
@IBOutlet var loginButton: UIButton!
@IBOutlet var loginButtonBottomConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
// Notifications for when the keyboard opens/closes
NotificationCenter.default.addObserver(
self,
selector: #selector(self.keyboardWillShow),
name: UIResponder.keyboardWillShowNotification,
object: nil)
NotificationCenter.default.addObserver(
self,
selector: #selector(self.keyboardWillHide),
name: UIResponder.keyboardWillHideNotification,
object: nil)
}
@objc func keyboardWillShow(_ notification: NSNotification) {
// Move the view only when the usernameTextField or the passwordTextField are being edited
if usernameTextField.isEditing || passwordTextField.isEditing {
moveViewWithKeyboard(notification: notification, viewBottomConstraint: self.loginButtonBottomConstraint, keyboardWillShow: true)
}
}
@objc func keyboardWillHide(_ notification: NSNotification) {
moveViewWithKeyboard(notification: notification, viewBottomConstraint: self.loginButtonBottomConstraint, keyboardWillShow: false)
}
func moveViewWithKeyboard(notification: NSNotification, viewBottomConstraint: NSLayoutConstraint, keyboardWillShow: Bool) {
// Keyboard's size
guard let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else { return }
let keyboardHeight = keyboardSize.height
// Keyboard's animation duration
let keyboardDuration = notification.userInfo![UIResponder.keyboardAnimationDurationUserInfoKey] as! Double
// Keyboard's animation curve
let keyboardCurve = UIView.AnimationCurve(rawValue: notification.userInfo![UIResponder.keyboardAnimationCurveUserInfoKey] as! Int)!
// Change the constant
if keyboardWillShow {
let safeAreaExists = (self.view?.window?.safeAreaInsets.bottom != 0) // Check if safe area exists
let bottomConstant: CGFloat = 20
viewBottomConstraint.constant = keyboardHeight + (safeAreaExists ? 0 : bottomConstant)
}else {
viewBottomConstraint.constant = 20
}
// Animate the view the same way the keyboard animates
let animator = UIViewPropertyAnimator(duration: keyboardDuration, curve: keyboardCurve) { [weak self] in
// Update Constraints
self?.view.layoutIfNeeded()
}
// Perform the animation
animator.startAnimation()
}
}
Code language: Swift (swift)
You can find the final project here
If you have any questions, please feel free to leave a comment below