keyboardFrameEndUserInfoKey not showing correct values? - ios

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.

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).

Related

Move Button & View when Keyboard Appears in Swift

I am trying to move a button that is initially at the bottom of the view, but when a text field is selected and the keyboard rendered, move the button up with it.
If you have used the app Robinhood, its the same functionality when signing in or signing up.
Several other posts have not been able to solve this for me.
Move view with keyboard using Swift
Is there an external repo or solution that has already solved this feature?
First of write the below code into the ui view extension.
extension UIView {
func bindToKeyboard() {
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange(_:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
}
#objc func keyboardWillChange(_ notification: NSNotification) {
let duration = notification.userInfo![UIResponder.keyboardAnimationDurationUserInfoKey] as! Double
let curve = notification.userInfo![UIResponder.keyboardAnimationCurveUserInfoKey] as! UInt
let begginingFrame = (notification.userInfo![UIResponder.keyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
let endFrame = (notification.userInfo![UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
let deltaY = endFrame.origin.y - begginingFrame.origin.y
UIView.animateKeyframes(withDuration: duration, delay: 0.0, options: UIView.KeyframeAnimationOptions(rawValue: curve), animations: {
self.frame.origin.y += deltaY
}, completion: nil)
}
}
Then use that function like i have used below.
override func viewDidLoad() {
super.viewDidLoad()
yourButtonName.bindToKeyboard()
}
Hope it will be the right solution for you.

keyboardWillShowNotification called twice when dismissed

I've subscribed to keyboardWillShowNotification and keyboardWillHideNotification to move around my UI. I've noticed that when I dismiss the keyboard by tapping the "Go" button, keyboardWillShowNotification is called twice (thus reseting some of my constraints) however if dismiss by hitting return on the keyboard (MacBook) then it's not called twice.
How can I avoid it being called twice? Why is this behaviour even there? I can't find any mention of it (lots of references to it being called twice with input views...etc) but never when being dismissed.
Here's my code:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardNotification(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWasDismissed(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}
and...
#objc func keyboardNotification(notification: NSNotification) {
guard
let animationDuration = notification.userInfo?["UIKeyboardAnimationDurationUserInfoKey"] as? Double,
let animationCurve = notification.userInfo?["UIKeyboardAnimationCurveUserInfoKey"] as? NSNumber,
let frameEnd = notification.userInfo?["UIKeyboardFrameEndUserInfoKey"] as? CGRect,
let frameBegin = notification.userInfo?["UIKeyboardFrameBeginUserInfoKey"]
else {
print("No userInfo recived from NSNotification.Name.UIKeyboardWillShow")
return
}
print("WILL SHOW")
let margin = self.view.safeAreaLayoutGuide
constraintsWhenKeyboardVisible = [
boxOffice.leadingAnchor.constraint(equalTo: margin.leadingAnchor),
boxOffice.trailingAnchor.constraint(equalTo: margin.trailingAnchor),
boxOffice.bottomAnchor.constraint(equalTo: margin.bottomAnchor),
boxOffice.topAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -(frameEnd.height + 50))
]
NSLayoutConstraint.deactivate(boxOfficeFinalConstraints)
NSLayoutConstraint.activate(constraintsWhenKeyboardVisible)
UIView.animate(withDuration: animationDuration,
delay: TimeInterval(0),
options: UIView.AnimationOptions(rawValue: animationCurve.uintValue),
animations: {
self.boxOffice.answerField.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
self.view.layoutIfNeeded()
},
completion: nil)
}
#objc func keyboardWasDismissed(notification: NSNotification) {
guard
let animationDuration = notification.userInfo?["UIKeyboardAnimationDurationUserInfoKey"] as? Double,
let animationCurve = notification.userInfo?["UIKeyboardAnimationCurveUserInfoKey"] as? NSNumber
else {
print("No userInfo recived from NSNotification.Name.UIKeyboardWillShow")
return
}
print("WILL HIDE")
//print(notification)
NSLayoutConstraint.deactivate(self.constraintsWhenKeyboardVisible)
NSLayoutConstraint.activate(self.boxOfficeFinalConstraints)
UIView.animate(withDuration: animationDuration,
delay: TimeInterval(0),
options: UIView.AnimationOptions(rawValue: animationCurve.uintValue),
animations: {
self.boxOffice.answerField.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMinXMinYCorner]
self.view.layoutIfNeeded()
},
completion: nil)
}
I haven't solved the issue of keyboardWillShowNotification being posted when Return is hit on the iOS keyboard simulator but not on the hardware keyboard in the simulator, but I have modified my code so that when the keyboard is shown I don't add constraints, I simply modify the constant of the constraint using the height from the keyboard notification. This has solved it.
boxOfficeWhenKeyboardVisible[3].constant = -(frameEnd.height + 50)

Is it possible to use a pageviewcontroller inside a scrollview?

I have a pageviewcontroller, which scrolls horizontally, displaying questions and answers.
The contentView for the pageviewcontroller is populated with a scrollview, and inside of this is some stack views, one of which has a textView, for user entry.
When the user taps on the textView, the keyboard pops up, sometimes covering up the textView. When I try to scroll vertically, as I would expect scrollview to allow, the scrollview doesn't respond, I can only scroll horizontally to the next page view.
The problem is that when the keyboard pops up after the user taps in the textview, the textview may be hidden underneath the keyboard, so the user can't see what he's typing. I'd like to have the view scroll upward when he keyboard is tapped, so the user can see what is being typed.
That's called "Keyboard managing"
You have to:
1) Set observer for keyboard appear and keyboard disappear action
2) Update bottom constraint, when keyboard appears.
3) Update it again, when keyboard disappears.
4) Delete observers, when view will disappear.
Step by step instructions, for example here: Move view with keyboard using Swift
But my recommendation is to update constraints, not origins, as in instruction. Or, maybe, you can set an offset for content
you should set an bottom constraint into your stack view
and then controll drag that in your class like this :
class yourClassViewController: UIViewController {
// MARK: Properties
#IBOutlet weak var bottomConstraint: NSLayoutConstraint!
then in your viewDidLoad method write like this:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self,
selector: #selector(self.keyboardNotification(notification:)),
name: NSNotification.Name.UIKeyboardWillChangeFrame,
object: nil)
// Do any additional setup after loading the view.
}
and below that:
deinit {
NotificationCenter.default.removeObserver(self)
}
#objc func keyboardNotification(notification: NSNotification) {
if let userInfo = notification.userInfo {
let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
let endFrameY = endFrame?.origin.y ?? 0
let duration:TimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIViewAnimationOptions.curveEaseInOut.rawValue
let animationCurve:UIViewAnimationOptions = UIViewAnimationOptions(rawValue: animationCurveRaw)
if endFrameY >= UIScreen.main.bounds.size.height {
self.bottomConstraint?.constant = 0.0
} else {
self.bottomConstraint?.constant = endFrame?.size.height ?? 0.0
}
UIView.animate(withDuration: duration,
delay: TimeInterval(0),
options: animationCurve,
animations: { self.view.layoutIfNeeded() },
completion: nil)
}
}
for disappear keyboard in touchsBegan method you should write like this:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
Use notification observers to handle keyboard show and hide events:
// keyboard will show
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShowAction(_:)), name: .UIKeyboardWillShow, object: nil)
// keyboard will hide
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHideAction(_:)), name: .UIKeyboardWillHide, object: nil)
You can do something like this when the keyboard is about to appear:
// keyboard will show action
#objc private func keyboardWillShowAction(_ notification: NSNotification) {
currentScrollViewOffset = scrollView.contentOffset
let keyboardFrame = notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue
let keyboardHeight = keyboardFrame?.cgRectValue.height
let padding: CGFloat = 32
// if text field view would be obstructed by keyboard, apply minimum-needed scroll view offset
if activeTextFieldMaxYOnScreen! > (UIScreen.main.bounds.height - keyboardHeight! - padding) {
let temporaryOffset = activeTextFieldMaxYOnScreen! - (UIScreen.main.bounds.height - (keyboardHeight! + padding))
scrollView.setContentOffset(CGPoint(x: 0, y: currentScrollViewOffset.y + temporaryOffset), animated: true)
}
scrollView.addGestureRecognizer(tapToDismissKeyboard)
scrollView.isScrollEnabled = false
}
And then revert when the keyboard is dismissed:
// keyboard will hide action
#objc private func keyboardWillHideAction(_ notification: NSNotification) {
scrollView.setContentOffset(currentScrollViewOffset, animated: true)
scrollView.removeGestureRecognizer(tapToDismissKeyboard)
scrollView.isScrollEnabled = true
}
It's also good practice to remove observers in the deinit method:
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil)
Note that currentScrollViewOffset and activeTextFieldMaxYOnScreen are instance properties of the view controller. To get the activeTextFieldMaxYOnScreen value, you can get it from the text field delegate:
// text field should begin editing
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
// convert text field line's max-y to perceived max-y on the screen
let line = textField.viewWithTag(123)
activeTextFieldMaxYOnScreen = (line?.convert((line?.bounds.origin)!, to: nil).y)! + (line?.frame.height)!
return true
}

How to move bottom view to top of UIKeyboard

Hi I am trying to make a view's bottom align with the top of UIKeyboard.
Update 1: I have created a github project if you would like to give it a try: https://github.com/JCzz/KeyboardProject
Note: I need the aView to be dynamic.
Update 2: Just pushed - to include using frames
I might have been looking at this for too long, I can not wrap my brain around it :-)
Do you know how?
How do I know if the UIKeyboard is on the way down or up?
If UIKeyboard is up, then how to align it with the view(attachKeyboardToFrame - see code).
I have found the following UIView extension:
import UIKit
extension UIView {
func bindToKeyboard(){
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
}
func unbindFromKeyboard(){
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
}
#objc
func keyboardWillChange(notification: NSNotification) {
guard let userInfo = notification.userInfo else { return }
let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as! Double
let curve = userInfo[UIKeyboardAnimationCurveUserInfoKey] as! UInt
let curFrame = (userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
let targetFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
// To get the total height of view
let topView = UIApplication.shared.windows.last
//
let attachKeyboardToFrame = Singleton.sharedInstance.attachKeyboardToFrame
let global_attachKeyboardToFrame = self.superview?.convert(attachKeyboardToFrame!, to: topView)
if (targetFrame.height + attachKeyboardToFrame!.height) > (topView?.frame.height)! {
self.frame.origin.y = -targetFrame.origin.y
}else{
}
}
}
You can achieve it using following Autolayout solution.
First you need UILayoutGuide that will be used simulate Keyboard aware bottom anchor, and a NSLayoutConstraint that will control this layout guide:
fileprivate let keyboardAwareBottomLayoutGuide: UILayoutGuide = UILayoutGuide()
fileprivate var keyboardTopAnchorConstraint: NSLayoutConstraint!
In the viewDidLoad add the keyboardAwareBottomLayoutGuide to the view and setup the appropriate contraints:
self.view.addLayoutGuide(self.keyboardAwareBottomLayoutGuide)
// this will control keyboardAwareBottomLayoutGuide.topAnchor to be so far from bottom of the bottom as is the height of the presented keyboard
self.keyboardTopAnchorConstraint = self.view.layoutMarginsGuide.bottomAnchor.constraint(equalTo: keyboardAwareBottomLayoutGuide.topAnchor, constant: 0)
self.keyboardTopAnchorConstraint.isActive = true
self.keyboardAwareBottomLayoutGuide.bottomAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor).isActive = true
Then use following lines to start listening to keyboard showing and hiding:
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShowNotification(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHideNotification(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
And finally, use following methods to control the keyboardAwareBottomLayoutGuide to mimic the keyboard:
#objc fileprivate func keyboardWillShowNotification(notification: NSNotification) {
updateKeyboardAwareBottomLayoutGuide(with: notification, hiding: false)
}
#objc fileprivate func keyboardWillHideNotification(notification: NSNotification) {
updateKeyboardAwareBottomLayoutGuide(with: notification, hiding: true)
}
fileprivate func updateKeyboardAwareBottomLayoutGuide(with notification: NSNotification, hiding: Bool) {
let userInfo = notification.userInfo
let animationDuration = (userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue
let keyboardEndFrame = (userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
let rawAnimationCurve = (userInfo?[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber)?.uint32Value
guard let animDuration = animationDuration,
let keybrdEndFrame = keyboardEndFrame,
let rawAnimCurve = rawAnimationCurve else {
return
}
let convertedKeyboardEndFrame = view.convert(keybrdEndFrame, from: view.window)
let rawAnimCurveAdjusted = UInt(rawAnimCurve << 16)
let animationCurve = UIViewAnimationOptions(rawValue: rawAnimCurveAdjusted)
// this will move the topAnchor of the keyboardAwareBottomLayoutGuide to height of the keyboard
self.keyboardTopAnchorConstraint.constant = hiding ? 0 : convertedKeyboardEndFrame.size.height
self.view.setNeedsLayout()
UIView.animate(withDuration: animDuration, delay: 0.0, options: [.beginFromCurrentState, animationCurve], animations: {
self.view.layoutIfNeeded()
}, completion: { success in
//
})
}
Now with all this set up, you can use Autolayout to constraint your views to keyboardAwareBottomLayoutGuide.topAnchor instead of self.view.layoutMarginsGuide.bottomAnchor (or self.view.bottomAnchor, whichever you use). keyboardAwareBottomLayoutGuide will automatically adjust to the keyboard showed or hidden.
Example:
uiTextField.bottomAnchor.constraint(equalTo: keyboardAwareBottomLayoutGuide.topAnchor).isActive = true
EDIT: Directly setting frames
While I strongly recommend using Autolayout, in cases when you cannot go with this, directly setting frames can be also a solution. You can use the same principle. In this approach you don't need layout guide, so you don't need any additional instance properties. Just use viewDidLoad to register for listening notifications:
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShowNotification(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHideNotification(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
Then implement methods that will react to these notifications:
#objc fileprivate func keyboardWillShowNotification(notification: NSNotification) {
adjustToKeyboard(with: notification, hiding: false)
}
#objc fileprivate func keyboardWillHideNotification(notification: NSNotification) {
adjustToKeyboard(with: notification, hiding: true)
}
fileprivate func adjustToKeyboard(with notification: NSNotification, hiding: Bool) {
let userInfo = notification.userInfo
let animationDuration = (userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue
let keyboardEndFrame = (userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
let rawAnimationCurve = (userInfo?[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber)?.uint32Value
guard let animDuration = animationDuration,
let keybrdEndFrame = keyboardEndFrame,
let rawAnimCurve = rawAnimationCurve else {
return
}
let convertedKeyboardEndFrame = view.convert(keybrdEndFrame, from: view.window)
let rawAnimCurveAdjusted = UInt(rawAnimCurve << 16)
let animationCurve = UIViewAnimationOptions(rawValue: rawAnimCurveAdjusted)
// we will go either up or down depending on whether the keyboard is being hidden or shown
let diffInHeight = hiding ? convertedKeyboardEndFrame.size.height : -convertedKeyboardEndFrame.size.height
UIView.animate(withDuration: animDuration, delay: 0.0, options: [.beginFromCurrentState, animationCurve], animations: {
// this will move the frame of the aView according to the diffInHeight calculated above
// of course here you need to set all the frames that would be affected by the keyboard (this is why I prefer using autolayout)
self.aView?.frame = (self.aView?.frame.offsetBy(dx: 0, dy: diff))!
// of course, you can do anything more complex than just moving the aView up..
})
}
In both cases, don't forget to unregister observing the notifications once the viewController is deinitialized to prevent retain cycles:
deinit {
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}

Swift move scroll view up when keyboard present

I'm working on an iOS app and currently all my elements are in a scroll view and when the keyboard is present I move the view up 250 pts. This solved my problem but the keyboard is always a different size per device.
How could I detect how far from the bottom of the screen my text field is and how tall the keyboard is?
You should observe the notification for showing and hiding the keyboard. And after that you can get the exact keyboard size and either shift or change the content insets of your scroll view. Here's a sample code:
extension UIViewController {
func registerForKeyboardDidShowNotification(scrollView: UIScrollView, usingBlock block: (NSNotification -> Void)? = nil) {
NSNotificationCenter.defaultCenter().addObserverForName(UIKeyboardDidShowNotification, object: nil, queue: nil, usingBlock: { (notification) -> Void in
let userInfo = notification.userInfo!
let keyboardSize = userInfo[UIKeyboardFrameBeginUserInfoKey]?.CGRectValue.size
let contentInsets = UIEdgeInsetsMake(scrollView.contentInset.top, scrollView.contentInset.left, keyboardSize!.height, scrollView.contentInset.right)
scrollView.scrollEnabled = true
scrollView.setContentInsetAndScrollIndicatorInsets(contentInsets)
block?(notification)
})
}
func registerForKeyboardWillHideNotification(scrollView: UIScrollView, usingBlock block: (NSNotification -> Void)? = nil) {
NSNotificationCenter.defaultCenter().addObserverForName(UIKeyboardWillHideNotification, object: nil, queue: nil, usingBlock: { (notification) -> Void in
let contentInsets = UIEdgeInsetsMake(scrollView.contentInset.top, scrollView.contentInset.left, 0, scrollView.contentInset.right)
scrollView.setContentInsetAndScrollIndicatorInsets(contentInsets)
scrollView.scrollEnabled = false
block?(notification)
})
}
}
extension UIScrollView {
func setContentInsetAndScrollIndicatorInsets(edgeInsets: UIEdgeInsets) {
self.contentInset = edgeInsets
self.scrollIndicatorInsets = edgeInsets
}
}
And in your UIViewController in which you want to shift the scrollview, just add next lines under the viewDidLoad() function
override func viewDidLoad() {
super.viewDidLoad()
registerForKeyboardDidShowNotification(scrollView)
registerForKeyboardWillHideNotification(scrollView)
}
I'm currently work on this and found a solution. First you need to add a notification to the view controller to identify whether the keyboard is on or not. For that you need to register this notification in viewDidLoad().
override func viewDidLoad() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardDidShow:", name: UIKeyboardWillChangeFrameNotification, object: nil)
}
And also don't forget to remove this notification, when the view controller removed from the view. So remove this notification on viewDidDisappear().
override func viewDidDisappear(animated: Bool) {
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillChangeFrameNotification, object: nil)
}
And the final thing is to manage the scroll view when the keyboard is on or off. So first you need to identify the keyboard height.Then for pretty smooth animation, you can use keyboard animation mood and duration time to animate your scroll view.
func keyboardDidShow(notification: NSNotification) {
if let userInfo = notification.userInfo {
let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue()
let duration:NSTimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
let animationCurveRaw = animationCurveRawNSN?.unsignedLongValue ?? UIViewAnimationOptions.CurveEaseInOut.rawValue
let animationCurve:UIViewAnimationOptions = UIViewAnimationOptions(rawValue: animationCurveRaw)
if endFrame?.origin.y >= UIScreen.mainScreen().bounds.size.height {
//isKeyboardActive = false
UIView.animateWithDuration(duration,
delay: NSTimeInterval(0),
options: animationCurve,
animations: {
// move scroll view height to 0.0
},
completion: { _ in
})
} else {
//isKeyboardActive = true
UIView.animateWithDuration(duration,
delay: NSTimeInterval(0),
options: animationCurve,
animations: {
// move scroll view height to endFrame?.size.height ?? 0.0
},
completion: { _ in
})
}
}
}
#noir_eagle answer seems right.
But there may be is a simpler solution. Maybe you could try using IQKeyboardManager. It allows you to handle these keyboard things in a simple and seamless way.
I think you really should, at least, spend few minutes looking at it.

Resources