Reputation: 141
I have a text input field at the bottom of my view, which I'm trying to animate up and down to stay on top of the keyboard.
func setupKeyboardObservers() {
NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardWillChangeFrame), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardDidShow), name: UIResponder.keyboardDidShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardWillChangeFrame), name: UIResponder.keyboardWillHideNotification, object: nil)
}
@objc func handleKeyboardWillChangeFrame(notification: NSNotification) {
let keyboardFrame = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
let keyboardDuration = (notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double)
print(keyboardFrame)
orderDetailView?.textInputViewBottomAnchor?.constant = -keyboardFrame!.height
UIView.animate(withDuration: keyboardDuration!) {
self.view.layoutIfNeeded()
}
}
OrderDetailView is the view for the viewcontroller.
The textinputview is the part that animates, and it works correctly when the keyboard first shows up, but does not animate back when I send a message and the keyboard resigns first responder, nor if I resignfirstresponder by clicking outside the keyboard.
When I print the cgrect value from keyboardFrameEndUserInfoKey, it returns the same frame value as when the keyboard is present (instead of 0).
This only seems to work properly when I drag down the keyboard from the view.
Thanks for your help.
Upvotes: 4
Views: 3645
Reputation: 16774
In your case the height is still non-zero when keyboard hides which I assume is your issue. You need to convert keyboard frame to your view coordinate system and setup constraints according to that. Check the following:
@objc private func onKeyboardChange(notification: NSNotification) {
guard let info = notification.userInfo else { return }
guard let value: NSValue = info[UIKeyboardFrameEndUserInfoKey] as? NSValue else { return }
let newFrame = value.cgRectValue
if let durationNumber = info[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber, let keyboardCurveNumber = info[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber {
let duration = durationNumber.doubleValue
let keyboardCurve = keyboardCurveNumber.uintValue
UIView.animate(withDuration: duration, delay: 0, options: UIViewAnimationOptions(rawValue: keyboardCurve), animations: {
self.updateFromKeyboardChangeToFrame(newFrame)
}, completion: { _ in
// After animation
})
} else {
self.updateFromKeyboardChangeToFrame(newFrame)
}
}
private func updateFromKeyboardChangeToFrame(_ keyboardFrame: CGRect) {
let view: UIView! // Whatever view that uses bottom constraint
let bottomConstraint: NSLayoutConstraint! // Your bottom constraint
let constant = view.bounds.height-max(0, view.convert(keyboardFrame, from: nil).origin.y)
bottomConstraint.constant = max(0, constant)
view.layoutIfNeeded()
}
In your case you seem to use
let view = self.view
let bottomConstraint = orderDetailView?.textInputViewBottomAnchor
and it depends on how you define your constraint but it seems you will need to use negative values as bottomConstraint.constant = -max(0, constant)
.
Upvotes: 7