Swift 3 move View when keyboard is shown - wrong KeyboardSize - ios

I'm currently struggling with moving my view when iOS is showing/hiding the keyboard.
(Sorry, didn't know how to format code highlighting)
I implemented the following observers:
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
And these functions:
func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y != 0 {
self.view.frame.origin.y += keyboardSize.height/2
}
}
}
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y == 0 {
self.view.frame.origin.y -= keyboardSize.height/2
}
}
}
And I added an extension:
extension UIViewController {
func hideKeyboardWhenTappedAround() {
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
}
func dismissKeyboard() {
view.endEditing(true)
}
}
So all this together works pretty well.
However, I have a problem once the keyboards thinks it'd be a good idea to show the word suggestions. The keyboard gets higher and the view wanders down too far.
This mostly happens when the users switch from a Numbers-Only keyboard into another textfield where it's a normal keyboard (sometimes with the suggestions).
So, is there a way to know whether the keyboard currently shows the word suggestions (or whatever other 'normal' height divergence)?
I thought about using a global var for the keyboard height of the keyboardWillShow function but that won't work since I'd either move to far or to less when they switch between textfields with different keyboard heights (see number pad).
Any help is highly appreciated! Thanks in advance!
EDIT:
Ah! I think storing the willShowKeyboard-height in a global var and comparing it to the willHideKeyboard-height might work. I'm still open for smoother solutions though!

The key UIKeyboardFrameBeginUserInfoKey doesn't give you the final size - as the name hints "Begin".
Instead try using it's sister UIKeyboardFrameEndUserInfoKey - which will give you the keyboards final size after end animation.
Note it doesn't take rotation into account https://developer.apple.com/reference/uikit/uikeyboardframeenduserinfokey

I ended storing the keyboardHeight from keboardWillShow into a global var and take that variable to move it back in keboardWillHide. And I'm checking whether the last keyboardHeight is equal to the current to move my view if needed.
Not too beautiful. But hey, it works!
// keyboardHeight is a global var.
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y == 0 ||
(keyboardHeight != keyboardSize.height/2)
{
self.view.frame.origin.y = 0
self.view.frame.origin.y -= keyboardSize.height/2
keyboardHeight = keyboardSize.height/2
}
}
}
func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
_ = keyboardSize.height/2 // to surpress warnings...
if self.view.frame.origin.y != 0 {
self.view.frame.origin.y += keyboardHeight //keyboardSize.height/2
}
}
}
I don't know about that self.view.frame.origin.y = 0 in keyboardWillShow. I think I'll consider only moving it the difference instead setting it to 0 each time. But at the moment everythink works as it should.

Related

Move screen up when keyboard appears in iPhone X

I use code below to move screen up and down when keyboard show and hide
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector:#selector(ViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
#objc func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y == 0{
self.view.frame.origin.y -= keyboardSize.height
}
}
}
#objc func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y != 0{
self.view.frame.origin.y += keyboardSize.height
}
}
}
It works in all devices except iPhone x (I think the problem is iPhone X bottom wasted space)
Problem is that keyboard size change before shown and after that and cause the view go down and down and down...
Can anyone help?
You should also consider safe area insets
let endFrame = (userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue ?? CGRect.zero
let bottomSpace = keyboardValues.endFrame.maxY - keyboardValues.endFrame.minY - self.view.safeAreaInsets.bottom
And you should not be additive. These notifications may be posted multiple times. Use absolute values.
Here it is,
Use IQKeyboardManagerSwift
AppDelegate.swift settings
IQKeyboardManager.shared.enable = true
IQKeyboardManager.shared.enableAutoToolbar = false
In My ViewController
func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
print(#function)
if DeviceType.checkiPhoneX() {
IQKeyboardManager.shared.keyboardDistanceFromTextField = 40.0
}
return true
}
func textViewDidEndEditing(_ textView: UITextView) {
if DeviceType.checkiPhoneX() {
IQKeyboardManager.shared.keyboardDistanceFromTextField = 0.0
}
}
This is how i manage Keyboard for X.
Hope this may help you.
Use UIKeyboardFrameEndUserInfoKey instead of UIKeyboardFrameBeginUserInfoKey.

Swift Table View (Chat UI) if keyboard was shown, whole view getting lower

i try to create a Chat UI. Therefore i added a Tableview and a textfield below. If a user wants to type in the textfield, the whole view should go up and thats work fine. If the user ends typing and the keyboard should be hidden, the first time the view moves a bit lower and the seccond time it moves lower as well. It looks like the following screenshots:
is used the following code:
override func viewDidLoad() {
super.viewDidLoad()
self.myChat.separatorStyle = .none
getChatId()
myChat.allowsSelection = false;
newMessage.delegate = self
// Do any additional setup after loading the view.
NotificationCenter.default.addObserver(self, selector: #selector(ChatViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(ChatViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
#objc func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y == 0{
self.view.frame.origin.y -= keyboardSize.height
}
}
}
#objc func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y != 0{
self.view.frame.origin.y += keyboardSize.height-1
}
}
}[![normally it should look like this][1]][1]
regards
Change this function with my change.
#objc func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y != 0{
self.view.frame.origin.y = 0
}
}
}

screen is black when move view in swift4

I have general message view controller I am sending message using textfield but keyboard is covered the text fields. I can refer
Move view with keyboard using Swift
this link added some code but after that some portion shows black. how to avoid the screen black after sending the message
this is code
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(GenralMessageViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(GenralMessageViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
#objc func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y == 0 {
self.view.frame.origin.y -= keyboardSize.height
}
}
}
#objc func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y != 0{
self.view.frame.origin.y += keyboardSize.height
}
}
}
Your code is just wrong. You have no business changing the frame of self.view. The main view of a view controller must be left in place where it is. Wrap your interface in a subview of the main view and move the subview (if you insist on using this approach to avoiding being covered by the keyboard).
Try changing your end y origin back to 0 instead of a calculated value.
#objc func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y != 0{
self.view.frame.origin.y == 0
}
}
}

View didn't up by keyboard on second click into text field

I have an iOS app with login screen(mobile and password). Let's imagine a case:
User tap the phone number text field and number keyboard appears, view is up with keyboard.
User fill phone number and tap password text field, keyboard changes from number layout yo standard.
User tap Return button on keyboard, and keyboard dismiss, view is down.
User tap phone number text field or password on more time for data correction
And at stage 4 the keyboard appear, BUT view is not up anymore, and when I tap return button on keyboard, the view drop even down with black line on top of screen. How I can fix that problem? in code I have this:
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
and in viewDidLoad:
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
and
#objc func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y == 0 {
self.view.frame.origin.y -= keyboardSize.height/3
}
}
}
#objc func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y != 0 {
self.view.frame.origin.y += keyboardSize.height/3
}
}
}
Try this
#objc func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y == 0 {
self.view.frame.origin.y -= keyboardSize.height/3
}
}
}
#objc func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y != 0 {
self.view.frame.origin.y = 0
}
}
}
I would suggest you to use the IQKeyboardmanager.It will take care of these things automatically
GitHub link - IQKeyboardManger

Move the screen with keyboard

I want to move the screen upwards when I show the keyboard and move to the bottom when I hide the keyboard.
I use this code:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
#objc func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y == 0{
self.view.frame.origin.y -= keyboardSize.height
}
}
}
#objc func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y != 0{
self.view.frame.origin.y += keyboardSize.height
}
}
}
But I have this problem: http://gph.is/2AvLo1A
On this screen I tap on textField to show keyboard and tap on empty space to hide keyboard several times. My screen crawls down, and the black background fills the entire screen. How to fix it?
Update:
http://gph.is/2F7nux5
After shown the keyboard I have new empty space at the bottom. And if I scroll I can see it: https://imgur.com/a/fFyEC
Hook the bottom constraint of the scrollview to the superViews's bottom and drag it as IBOutlet and do this
#objc func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
self.scrollviewBottomCon.constant = keyboardSize.height
self.view.layoutIfNeeded()
self.scrollView.scrollRectToVisible((self.textfeild.frame), animated: true)
}
}
#objc func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
self.scrollviewBottomCon.constant = 0
self.view.layoutIfNeeded()
}
}
Try not to move the entire view up and down, rather the components you really need to move. I'd suggest using a view to contain the elements you want to move.
I have a use case where I have a coupe text fields and a submit button beneath in a container view. When the keyboard appears, I wanted the two text fields and button to rise. Here's how I handled that.
#objc func keyboardWillAppear(_ notification: NSNotification) {
if let userInfo = notification.userInfo,
let keyboardFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
// Calculate how much to move
let submitButtonHeight = self.submitButton.frame.size.height
var submitButtonOrigin = self.view.convert(self.submitButton.frame.origin, from: self.containerView)
submitButtonOrigin.y = submitButtonOrigin.y + submitButtonHeight + 4.0
var visibleRect = self.view.frame
visibleRect.size.height -= keyboardFrame.height
// If keyboard covers the button, move the container up
if !visibleRect.contains(submitButtonOrigin) {
let keyboardMove = submitButtonOrigin.y - visibleRect.size.height
if keyboardMove > 0 {
UIView.animate(withDuration: 0.3) {
self.containerView.transform = CGAffineTransform(translationX: 0, y: -keyboardMove)
}
}
}
}
}
#objc func keyboardWillDisappear(_ notification: NSNotification) {
UIView.animate(withDuration: 0.3) {
self.containerView.transform = CGAffineTransform.identity
}
}
This may or may not be a solution for you, but can provide another option.

Resources