Synchronising layout constraint animation with device rotation - ios

I'm struggling with textfield bottom constrain animation when the keyboard is up and the device is rotating. Basically I need to synchronize the textfield position change animation with the rotation animation. Just like in the video here: https://www.youtube.com/watch?v=bbMYVEdodJQ
I'm using viewWillTransition(to size:)-method to get animation duration and curve parameters used for device rotation animation. That' ok.
Then I'm using Notification center notification called UIKeyboardWillChangeFrame to inform me the keyboard size so I can move the textField accordingly. Thats ok too. Unfortunately the UIKeyboardWillChangeFrame-notification happens always after the viewWillTransition(to size:)-method is called.
Any advice how I should synchronize my animation? Below is the code where the animation is out of sync.
func setKeyboardObservers() {
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange(_:)), name: NSNotification.Name.UIKeyboardWillChangeFrame , object: nil)
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { (context) in
self.deviceRotating = true
self.animationDuration = context.transitionDuration
self.animationCurve = UInt(context.completionCurve.rawValue)
}) { (_) in
self.deviceRotating = false
}
}
#objc func keyboardWillChange(_ notification: NSNotification) {
let keyboardFrame = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
if deviceRotating {
textFieldBottomContstrain.constant = -keyboardFrame.height
UIView.animateKeyframes(withDuration: animationDuration, delay: 0, options: UIViewKeyframeAnimationOptions(rawValue: animationCurve), animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
}

Related

Keyboard covers text fields at the bottom of my view

I have searched
here: Move a view up only when the keyboard covers an input field
here: Move textfield when keyboard appears swift
here: How to make a UITextField move up when keyboard is present?
and here: https://developer.apple.com/library/content/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html
Unfortunately, all of the links and seemingly everywhere else I can find do not give a good clean solution that I am looking for. They are either outdated with Obj-c code, or plain do not work in the current iteration of Xcode 9 with Swift 4.
How do I manage a keyboard that is covering text fields at the bottom of my view? I want the screen's view to move only when they keyboard is covering the text field view, without using a scroll view, with the ability to animate this to make it look pretty rather than have it just snap, and most importantly I do not want to use an outside library.
CoreAnimation libraries from Apple are great and all, but all of the sample code is outdated and in objective c which is deprecated at this point (I cannot believe that Apple isn't updating their documentation).
If someone could point me in the right direction to updated and current code or a library from Apple I am missing that will specifically address this issue, it would be much appreciated.
This code will work, making your textField animating to above keyboard if its frame intersects with that of keyboard and animating back to original position on keyboard hide.
#IBOutlet weak var textField: UITextField!
var offsetY:CGFloat = 0
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardFrameChangeNotification(notification:)), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
}
func keyboardFrameChangeNotification(notification: Notification) {
if let userInfo = notification.userInfo {
let endFrame = userInfo[UIKeyboardFrameEndUserInfoKey] as? CGRect
let animationDuration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? Double ?? 0
let animationCurveRawValue = (userInfo[UIKeyboardAnimationCurveUserInfoKey] as? Int) ?? Int(UIViewAnimationOptions.curveEaseInOut.rawValue)
let animationCurve = UIViewAnimationOptions(rawValue: UInt(animationCurveRawValue))
if let _ = endFrame, endFrame!.intersects(self.textField.frame) {
self.offsetY = self.textField.frame.maxY - endFrame!.minY
UIView.animate(withDuration: animationDuration, delay: TimeInterval(0), options: animationCurve, animations: {
self.textField.frame.origin.y = self.textField.frame.origin.y - self.offsetY
}, completion: nil)
} else {
if self.offsetY != 0 {
UIView.animate(withDuration: animationDuration, delay: TimeInterval(0), options: animationCurve, animations: {
self.textField.frame.origin.y = self.textField.frame.origin.y + self.offsetY
self.offsetY = 0
}, completion: nil)
}
}
}
}
You can use IQKeyboardManagerSwift to solve your issue easily and fast.
Use below pod in your pod file Which give support to Swift 4.
pod 'IQKeyboardManagerSwift', '5.0.0'
Here is link to implement IQKeyboardManagerSwift.
https://github.com/hackiftekhar/IQKeyboardManager
Thanks!!!
Add an extensio to Uiview:
import UIKit
//Binding view to keyboard changes
extension UIView {
func bindToKeyboard(){
NotificationCenter.default.addObserver(self, selector: #selector(UIView.keyboardWillChange(_:)), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
}
#objc func keyboardWillChange(_ notification: NSNotification) {
let duration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double
let curve = notification.userInfo![UIKeyboardAnimationCurveUserInfoKey] as! UInt
let curFrame = (notification.userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
let targetFrame = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
let deltaY = targetFrame.origin.y - curFrame.origin.y
UIView.animateKeyframes(withDuration: duration, delay: 0.0, options: UIViewKeyframeAnimationOptions(rawValue: curve), animations: {
self.frame.origin.y += deltaY
},completion: {(true) in
self.layoutIfNeeded()
})
}
}
worked perfectly for me:
http://www.seemuapps.com/move-uitextfield-when-keyboard-is-presented
If delegates are set right,
func textFieldDidBeginEditing(_ textField: UITextField) {
moveTextField(textField, moveDistance: -250, up: true)
}
// Finish Editing The Text Field
func textFieldDidEndEditing(_ textField: UITextField) {
moveTextField(textField, moveDistance: -250, up: false)
}
// Hide the keyboard when the return key pressed
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
// Move the text field in a pretty animation!
func moveTextField(_ textField: UITextField, moveDistance: Int, up: Bool) {
let moveDuration = 0.3
let movement: CGFloat = CGFloat(up ? moveDistance : -moveDistance)
UIView.beginAnimations("animateTextField", context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(moveDuration)
self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
UIView.commitAnimations()
}
This piece of code worked for me.
(In case of multiple textfields)
I have implemented only for the textfields which are at the bottom without using notification observer.
If you are using ScrollView, this code might help you(scrollViewDidScroll is optional)
func scrollViewDidScroll(_ scrollView: UIScrollView) {
scrollView.contentSize = CGSize(width: self.scrollView.frame.size.width, height: (scrollView.frame.size.height + 300)) } // To be more specific, I have used multiple textfields so wanted to scroll to the end.So have given the constant 300.
func textFieldDidBeginEditing(_ textField:UITextField){
self.scrollView.setContentOffset(textField.frame.origin, animated: true)}
and if you want to set the textfields position according to the view,
try this:
func textFieldDidBeginEditing(_ textField:UITextField){
textField.frame.origin.y = textField.frame.origin.y - 150 //(If have not used contentsizing the scroll view then exclude this line)default origin takes the texfield to the top of the view.So to set lower textfields to proper position have used the constant 150.
self.scrollView.setContentOffset(textField.frame.origin, animated: true)}
To do specifically for textfields at the bottom. Check their tag value textfield.tag in textFieldDidBeginEditing
func textFieldDidBeginEditing(_ textField:UITextField){
if textField.tag = 4 { //tag value of the textfield which are at the bottom
self.scrollView.setContentOffset(textField.frame.origin, animated: true)}}
If you are using single textfield go with notification observer

keyboard hide my textfield and my solution code didn't work

Hello there are many questions about keyboard hide textfield problem
I saw many solutions between simple one and little bit complex one
I tried to move up my view by origin.y
when keyboard show
self.view.frame.origin.y = -150
keyboard hide
self.view.frame.origin.y = 0
sometimes it works but not now
when keyboard show, view moves up but it has black area when it downs like this
keyboard shows up
keyboard down
of course it occurs in my real device what's the problem?
so i use CGAffineTransform like this, and it works!
#objc func keyboardWillHide(_ sender: Notification){
self.view.transform = .identity
}
#objc func keyboardWillShow(_ sender: Notification){
guard let userInfo = sender.userInfo as? [String:Any] else {return}
guard let keyboardFrame = userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue else {return}
self.view.transform = CGAffineTransform(translationX: 0, y: -keyboardFrame.cgRectValue.height + 60)
}
is it fine? or has potential error?
This will not hide your keyboard.You can scroll the textfield when keyboard appears, Just -
Make a UIScrollView in your Storyboard
And write this code in your ViewController -
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var scrollView: UIScrollView!
//MARK: - // left it as it is, don't make it's connection with storyboard
#IBOutlet weak var activeTextField: UITextField?
override func viewWillAppear(_ animated: Bool) {
self.setNotificationKeyboard()
}
// Notification when keyboard show
func setNotificationKeyboard () {
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWasShown(notification:)), name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillBeHidden(notification:)), name: .UIKeyboardWillHide, object: nil)
}
//MARK: - get current text field
func textFieldDidBeginEditing(_ textField: UITextField) {
activeTextField = textField;
}
func textFieldDidEndEditing(_ textField: UITextField) {
activeTextField = nil
}
//MARK: - when show keyboard increase height of scroll view
func keyboardWasShown(notification: NSNotification)
{
var info = notification.userInfo!
let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
let contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize!.height+10, 0.0)
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
var aRect : CGRect = self.view.frame
aRect.size.height -= keyboardSize!.height
if let activeField = self.activeTextField
{
if (!aRect.contains(activeField.frame.origin))
{
self.scrollView.scrollRectToVisible(activeField.frame, animated: true)
}
}
}
//MARK: - when keyboard hide reduce height of scroll view
func keyboardWillBeHidden(notification: NSNotification){
let contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0,0.0, 0.0)
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
self.view.endEditing(true)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if let nextField = textField.superview?.superview?.viewWithTag(textField.tag + 1) as? UITextField {
nextField.becomeFirstResponder()
} else {
textField.resignFirstResponder()
}
return false
}
}
Manipulating UIViewController's base view is not a good idea. You can and most likely will run into some wierd issues.
I recommend that you embed everything that needs to be moved into it's own UIView: Select all subviews, Editor -> Embed -> View. Add all required constraints and create an IBOutlet for the bottom constraint - inputViewBottomConstraint.
Then in your view controller add this:
fileprivate func subscribeToNotifications() {
NotificationCenter.default.addObserver(forName: .UIKeyboardWillShow, object: nil, queue: nil) { [weak self] notification in
self?.updateInputViewConstraints(withUserInfo: notification.userInfo, isAppearing: true)
}
NotificationCenter.default.addObserver(forName: .UIKeyboardWillHide, object: nil, queue: nil) { [weak self] notification in
self?.updateInputViewConstraints(withUserInfo: notification.userInfo, isAppearing: false)
}
}
fileprivate func updateInputViewConstraints(withUserInfo userInfo: [AnyHashable: Any]?, isAppearing: Bool) {
let keyboardFrame = userInfo?[UIKeyboardFrameEndUserInfoKey] as! CGRect
let animationDuration = userInfo?[UIKeyboardAnimationDurationUserInfoKey] as! Double
let animationCurve: UIViewAnimationOptions = {
let curve = userInfo?[UIKeyboardAnimationCurveUserInfoKey] as! UInt
return UIViewAnimationOptions(rawValue: curve)
}()
inputViewBottomConstraint.constant = isAppearing ? keyboardFrame.size.height : 0
UIView.animate(withDuration: animationDuration, delay: 0, options: animationCurve, animations: {
self.view.layoutSubviews()
}, completion: nil)
}
There is a very good library to manage keyboard in iOS apps
https://github.com/hackiftekhar/IQKeyboardManager
You should use this library.
Since part of your screen is getting hidden behind the keyboard, you
can use a Scroll View - the user will then be able to scroll up and
down to access the whole screen.
In your xib or Storyboard file, add a UIScrollView. Assuming you already have elements added, a quick way to do this will be -
Select all the elements on the screen you want to put inside the
scrollable area. From top bar in XCode, select Editor -> Embed In
-> View. You can rename this view as ContentView.
Select the ContentView and now from top bar in XCode, select Editor -> Embed In
-> Scroll View
For the Scroll View, set top, bottom, leading and trailing constraints to 0 (from the Superview/Safe Area whichever
you are using).
For the ContentView, set top, bottom, left and
right margins to 0, also set center horizontally and vertically (all from the scrollView as it is the superView).
Now you can add
all your views in that ContentView, and the contentSize of the
scrollView will be automatically resized accordingly. For your case - Adjust rest of the constraints on your
screen accordingly. Say, by giving the vertical spacing of the top
most element of screen with the Content View (Similar process for
the bottom most element). This step is same as designing any UI
screen in iOS.
You can optionally do this step to dismiss the
scrollView when user starts scrolling. Select the Scroll View, tap
on Attributes Inspector and set Keyboard option to "Dismiss on
drag".
If you still want to move the UI elements based on whether keyboard is
present/shown or hidden. Then, you can move the content View's
position from the Scroll View by setting scrollView's
setContentOffset property.
class SampleViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
addKeyboardNotifications()
}
func addKeyboardNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(SampleViewController.keyboardWillShow(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(SampleViewController.keyboardWillHide(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
#objc func keyboardWillShow(_ notification: Notification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.size {
let keyboardHeight = keyboardSize.height
// This will move the contentView upwards by the same amount as the height of the keyboard which appears on screen
scrollView.setContentOffset(CGPoint(x: 0.0, y: keyboardHeight), animated: true)
}
}
#objc func keyboardWillHide(_ notification: NSNotification) {
// This will move the contentView back to it's original position.
scrollView.setContentOffset(CGPoint(x: 0.0, y: 0.0), animated: true)
}
}
Hope this helps! :)

How to get the screen height after orientation change?

I'm getting some strange results when querying UIScreen.main.bounds.height after an orientation change. Maybe this isn't the correct way to do it.
I have an observer that listens for the NSNotification.Name.UIDeviceOrientationDidChange event:
NotificationCenter.default.addObserver(self, selector: #selector(self.orientationChange), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
This calls a function that sets a constraint to 75% of the new screen height. This works fine on iPhone but iPad returns the wrong screen height.
If the iPad is in landscape orientation UIScreen.main.bounds.height will return a value equal to the height in portrait orientation and vice versa.
func orientationChange() {
// This will print the correct value on iPhone and iPad.
if UIDevice.current.orientation.isLandscape {
print("Landscape")
} else {
print("Portrait")
}
let screenSize = UIScreen.main.bounds
let screenHeight = screenSize.height
self.searchViewHeightConstraint.constant = screenHeight * 0.75
// Correct value on iPhone. Incorrect on iPad.
print("screenHeight: '\(screenHeight)'")
UIView.animate(withDuration: 0.6, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: .curveEaseOut, animations: {
self.searchView.superview?.layoutIfNeeded()
}, completion: nil)
}
I've also come across the viewWilltransition method of monitoring orientation change but this behaves in the exact opposite way to the method above. ie. the height is correct on iPad but incorrect on iPhone:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
let screenSize = UIScreen.main.bounds
let screenHeight = screenSize.height
self.searchViewHeightConstraint.constant = screenHeight * 0.75
// Correct value on iPad. Incorrect on iPhone.
print("screenHeight: '\(screenHeight)'")
}
What is the reason for this inconsistent behaviour between iPhone and iPad and is using NotificationCenter the correct approach to monitoring orientation change?
What you should be using is
viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator). This will give you the size and is WAY more reliable than using a notification.
Also, if you wanted to do animations, inside of the UIViewControllerTransitionCoordinator you can leverage the method animate(alongsideTransition animation: ((UIViewControllerTransitionCoordinatorContext) -> Swift.Void)?, completion: ((UIViewControllerTransitionCoordinatorContext) -> Swift.Void)? = nil) -> Bool
This works for me in Swift 2.3 for both iPad and iPhone.
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
print(size.height)
if UIDevice.currentDevice().orientation.isLandscape {
print("Landscape")
print(size.height)
}
else {
print("Portrait")
print(size.height)
}
}

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.

UIKeyboardWillShowNotification is called three times

I need to move a UIView up as soon as the keyboard will become visible. But the problem I'm facing right now is that my UIKeyboardWillShowNotification is called three times when I'm using a custom Keyboard (e.g. SwiftKey) which results in a bad animation.
Is there a way to handle only the last notification? I could easily dodge the first one because the height is 0, but the second one looks like a valid height and I don't find an answer on how to solve this.
Here is what I've so far:
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillAppear:", name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillDisappear:", name: UIKeyboardWillHideNotification, object: nil)
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func keyboardWillAppear(notification: NSNotification){
print("keyboard appear")
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue() {
print("with height: \(keyboardSize.height)")
if keyboardSize.height == 0.0 {
return
}
self.txtViewBottomSpace.constant = keyboardSize.height
UIView.animateWithDuration(0.4, animations: { () -> Void in
self.view.layoutIfNeeded()
})
}
}
func keyboardWillDisappear(notification: NSNotification){
print("Keyboard disappear")
self.txtViewBottomSpace.constant = 0.0
UIView.animateWithDuration(0.4, animations: { () -> Void in
self.view.layoutIfNeeded()
})
}
My Log output is:
keyboard appear
with height: 0.0
keyboard appear
with height: 216.0
keyboard appear
with height: 258.0
Keyboard disappear
So is there any way to only handle the third notification and "ignore" the first two?
Change the notification name UIKeyboardDidShowNotification and UIKeyboardDidHideNotification then solve the problem
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillAppear:", name: UIKeyboardDidShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillDisappear:", name: UIKeyboardDidHideNotification, object: nil)
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func keyboardWillAppear(notification: NSNotification){
print("keyboard appear")
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue() {
print("with height: \(keyboardSize.height)")
if keyboardSize.height == 0.0 {
return
}
self.txtViewBottomSpace.constant = keyboardSize.height
UIView.animateWithDuration(0.4, animations: { () -> Void in
self.view.layoutIfNeeded()
})
}
}
func keyboardWillDisappear(notification: NSNotification){
print("Keyboard disappear")
self.txtViewBottomSpace.constant = 0.0
UIView.animateWithDuration(0.4, animations: { () -> Void in
self.view.layoutIfNeeded()
})
}
I suggest to replace the static animation duration (0.4) with the animation duration of the keyboard, returned in the userInfo dictionary of the notification under UIKeyboardAnimationDurationUserInfoKey.
In this way your animation will be in sync with the keyboard animation. You can also retrieve the animation curve used by the keyboard with the UIKeyboardAnimationCurveUserInfoKey key.
func keyboardWillAppear(notification: NSNotification){
print("keyboard appear")
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue() {
let animationDuration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey]?.doubleValue;
print("with height: \(keyboardSize.height)")
if keyboardSize.height == 0.0 {
return
}
self.txtViewBottomSpace.constant = keyboardSize.height
UIView.animateWithDuration(animationDuration!, delay: 0.0, options: .BeginFromCurrentState, animations: { () -> Void in
self.view.layoutIfNeeded()
})
}
}
func keyboardWillDisappear(notification: NSNotification){
print("Keyboard disappear")
let animationDuration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey]?.doubleValue;
self.txtViewBottomSpace.constant = 0.0
UIView.animateWithDuration(animationDuration!, delay: 0.0, options: .BeginFromCurrentState, animations: { () -> Void in
self.view.layoutIfNeeded()
})
}
The reason for this is because keyboards can have different sizes, especially third party ones. So the first notification you receive will always be for the default system height, and any you receive after that will include the new heights of a third party keyboard extension if one is loaded. In order to get around this, in your method that handles the notification, you need to get the height originally, and then set that as a default height (I think 226). Then set a variable to this first height, and then for resulting calls to the notification method you can check if the new height is greater than the original height, and if it is you can find the delta, and then readjust your frames accordingly.
Set all bellow fields to NO can resolve this problem.
Capitalizaion: None
Correction: No
Smart Dashes: No
Smart insert: No
Smart Quote: No
Spell Checking: No

Resources