I know I'm a bit to late to the party, but since last answer was around 2 years ago I think I can provide more value to this question.
My example works on Swift 5.5+
, but should be okay with lower versions as well.
I my scenario I needed some sort of more universal, yet configurable solution, that would allow me to move view with keyboard and control spacings in any UIViewController
class that I have in project.
Here are steps, that you need to make in order for it to work:
Step one:
Create outlet for your view bottom constraint and define custom spacings for keyboard and your source view:
@IBOutlet weak var <<yourViewName>>BottomConstraint: NSLayoutConstraint! // Your view's bottom constraint, that should be connected to safe/area or superview
private let <<yourViewName>>BottomSpacing: CGFloat = 56 // Spacing between view and safe area/superview when keyboard is hidden
private let <<yourViewName>>KeyboardSpacing: CGFloat = 22 // Spacing between active keyboard and your view
Step two:
Register observers in our view controller that will track show/hide of keyboard (you can call this method in viewDidLoad
or init
depending on your needs) Please mind, that you have to remove observers manually in i.e. deinit
in older versions of Swift
:
private func addKeyboardObservers() {
// Notifications for when the keyboard show/hides
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)
}
Step three:
Add this code to your UIViewController
class. This code will be called as a result of NotificationCenter
observers:
@objc private func keyboardWillShow(_ notification: NSNotification) {
moveViewWithKeyboard(notification: notification,
keyboardWillShow: true,
viewBottomConstraint: <<yourViewName>>BottomConstraint,
activeKeyboardToViewSpacing: <<yourViewName>>KeyboardSpacing,
hiddenKeyboardToViewSpacing: <<yourViewName>>BottomSpacing)
}
@objc private func keyboardWillHide(_ notification: NSNotification) {
moveViewWithKeyboard(notification: notification,
keyboardWillShow: false,
viewBottomConstraint: <<yourViewName>>BottomConstraint,
activeKeyboardToViewSpacing: <<yourViewName>>KeyboardSpacing,
hiddenKeyboardToViewSpacing: <<yourViewName>>BottomSpacing)
}
Step Four:
Last part of puzzle, method, that gets keyboard size, calculates spacings and moves view accordingly.
Usually, in my projects I have a file named UIViewController+Keyboard.swift
with this code in extension
to UIViewController
. You can also add other code, that corresponds to keyboard management, like hiding it when user taps around, as in example below:
extension UIViewController {
func hideKeyboardWhenTappedAround() {
let tap = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
}
@objc
func dismissKeyboard() {
view.endEditing(true)
}
func moveViewWithKeyboard(notification: NSNotification,
keyboardWillShow: Bool,
viewBottomConstraint: NSLayoutConstraint,
activeKeyboardToViewSpacing: CGFloat,
hiddenKeyboardToViewSpacing: CGFloat) {
guard let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else { return }
let keyboardHeight = keyboardSize.height
let keyboardAnimationDuration = notification.userInfo![UIResponder.keyboardAnimationDurationUserInfoKey] as! Double
let keyboardAnimationCurve = UIView.AnimationCurve(rawValue: notification.userInfo![UIResponder.keyboardAnimationCurveUserInfoKey] as! Int)!
// Modifying spacing constants
if keyboardWillShow {
let safeAreaExists = self.view?.window?.safeAreaInsets.bottom != 0
// Default value in case something goes wrong with bottom spacings
let bottomConstant: CGFloat = 20
viewBottomConstraint.constant = keyboardHeight + (safeAreaExists ? 0 : bottomConstant) + activeKeyboardToViewSpacing
} else {
viewBottomConstraint.constant = hiddenKeyboardToViewSpacing
}
// Animating the view the same way the keyboard animates
let animator = UIViewPropertyAnimator(duration: keyboardAnimationDuration, curve: keyboardAnimationCurve) { [weak self] in
self?.view.layoutIfNeeded()
}
animator.startAnimation()
} }
After implementation of all this steps you should have desired and reusable behaviour with configurable spacings.
Hope that helps!