I am having a weird problem with moving my views to make room for an incoming keyboard. Basically, in my app, there is a button that performs a segue which pushes a new instance of a view controller that is embedded inside of a navigation controller modally. Within this first instance, my keyboard code works perfectly. The code is as follows:
func keyboardWillShow(sender: NSNotification) {
if !keyBoard {
self.view.frame.origin.y -= 200
}
keyBoard = true
}
func keyboardWillHide(sender: NSNotification) {
if keyBoard {
self.view.frame.origin.y += 200
}
keyBoard = false
}
with the following in viewDidLoad:
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name: UIKeyboardWillHideNotification, object: nil)
I also added a tapGestureRecognizer so that when the keyboard is showing and the user taps anywhere on the screen, the keyboard closes:
let tapped = UITapGestureRecognizer(target: self, action: "closeKeyboard")
tapped.numberOfTapsRequired = 1
self.view.addGestureRecognizer(tapped)
and
func closeKeyboard() {
self.view.endEditing(true)
}
So in the first instance, this code works perfectly. However, after I go back, calling self.dismissViewControllerAnimated, and, once I am on the original screen, click the button that calls performSegue again and push a new instance of this same view controller, the code breaks and the view no longer moves out of the way of the keyboard but just sort of stutters and bounces a little. I have no idea why this is happening and any help would be very much appreciated. Thanks!
Try putting a breakpoint on keyboardWillShow and keyboardWillHide, and tell me if both the methods are called when you go back to the second ViewController.
Related
I have an input accessory view called chatBoxView, which contains a custom growingTextView and couple of buttons to send the message. And this inputAccessoryView is setup in my main view controller.
override var inputAccessoryView: UIView? {
get {
return chatBoxView
}
}
override var canBecomeFirstResponder: Bool {
return true
}
So every time I go to this viewController, I see the chatBoxView at the bottom of my screen, which is wanted. However, the keyboard isn't visible yet (only the chatBoxView), but why does my KeyBoardWillShow notification keep getting called?
Also, when the keyboard is up, then disappears when I tap the outside, KeyboardWillDisappear function gets called, but KeyBoardWillShow function gets called right after.
Here are the function for the Keyboard notifications.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}
#objc func keyboardWillShow(notification: Notification) {
if let keyboardHeight = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.height {
print("Notification: Keyboard will show")
DispatchQueue.main.async {
self.tableView.setBottomInset(to: keyboardHeight - self.chatBoxView.frame.height)
self.tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .bottom, animated: true)
}
}
}
#objc func keyboardWillHide(notification: Notification) {
print("Notification: Keyboard will hide")
tableView.setBottomInset(to: 0.0)
}
So basically, I get the log "Notification: Keyboard will show", when the keyboard isn't showing and only the accessory view. Any ideas?
The problem is that you are throwing away important information in the UIResponder.keyboardFrameEndUserInfoKey. It is not enough just to look at the keyboard's height. You need to look at where the keyboard will be — look at its entire frame information. If the top of the keyboard is not going to be above the bottom of the screen, clearly you should not be doing anything. Similarly, if the keyboard frame is not going to be changing from where it was, you should not be doing anything.
In simulator, make sure you show software keyboard.
Click here or ⌘ + K
When a user taps on a button, I'd like the keyboard to pop up (which is easy), but I want a view that goes up along with it (sticking to the top of the keyboard). This view will be have a "send a message.." textfield. When the user pushes done, I want the keyboard to go away along with the view.
How do I make this view "stick" to the keyboard?
UITextFields have a property called inputAccessoryView
- Apple Documentation
- Relevant Stack Overflow Answer
This will pin whatever view you assign as that textfield's inputAccessoryView to the top of the keyboard.
Something important from the answer in that link to remember:
Note that the view you use should neither be in the view hierarchy elsewhere, nor should you add it to some superview, this is done for you.
go to your storyboard and add a view(lets call it topKeyboardView) at the bottom of your viewController. and give it the following constraints:
bottom space to bottom layout = 0
and then add the textfield*(i prefer using textView to make it change its height when the message gets too long...)*
and your button(send) on top of topKeyboardView.
lets code now..
go to your viewController.swift and add an IBOutlet to your textField and button and add this function:
//this is will tell if the keyboard hidden or not
func addKeyboardNotifications() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name:UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name:UIKeyboardWillHideNotification, object: nil)
}
// MARK:- Notification
func keyboardWillShow(notification: NSNotification) {
print("keyboard is up")
}
func keyboardWillHide(notification: NSNotification) {
print("keyboard is down")
}
in your viewDidLoad call the function:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
addKeyboardNotifications()
}
run it...
When a user taps on a button, I'd like the keyboard to pop up (which is easy), but I want a view that goes up along with it (sticking to the top of the keyboard). This view will be have a "send a message.." textfield. When the user pushes done, I want the keyboard to go away along with the view.
How do I make this view "stick" to the keyboard?
UITextFields have a property called inputAccessoryView
- Apple Documentation
- Relevant Stack Overflow Answer
This will pin whatever view you assign as that textfield's inputAccessoryView to the top of the keyboard.
Something important from the answer in that link to remember:
Note that the view you use should neither be in the view hierarchy elsewhere, nor should you add it to some superview, this is done for you.
go to your storyboard and add a view(lets call it topKeyboardView) at the bottom of your viewController. and give it the following constraints:
bottom space to bottom layout = 0
and then add the textfield*(i prefer using textView to make it change its height when the message gets too long...)*
and your button(send) on top of topKeyboardView.
lets code now..
go to your viewController.swift and add an IBOutlet to your textField and button and add this function:
//this is will tell if the keyboard hidden or not
func addKeyboardNotifications() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name:UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name:UIKeyboardWillHideNotification, object: nil)
}
// MARK:- Notification
func keyboardWillShow(notification: NSNotification) {
print("keyboard is up")
}
func keyboardWillHide(notification: NSNotification) {
print("keyboard is down")
}
in your viewDidLoad call the function:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
addKeyboardNotifications()
}
run it...
I know how to detect a normal keyboard being shown in Swift but I'm wondering if it's possible to detect the events inside a WKWebView because... if the app gets in the background, inputs lose focus but the "blur" event isn't triggered.
The idea is that I have a "navigation bar" in the app which gets pushed out of the view (upwards) when the keyboard is being shown, and I'd like to keep showing it. Knowing the keyboard is about 216px tall I'd just like to narrow the height of the content wrapper which is flex based by 216px but that's not really working since it is an animation which I can't really reproduce so it's smooth. I also can't detect when user went into emoji tab. I got rid of the autocorrect since I just set it to off for the input tag.
Try this code.
override viewWillAppear(){
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name:UIKeyboardWillShowNotification, object: nil);
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide"), name:UIKeyboardWillHideNotification, object: nil);
}
func keyboardWillHide(){
navigationController?.setNavigationBarHidden(false, animated: true)
}
func keyboardWillShow(notification: NSNotification) {
navigationController?.setNavigationBarHidden(true, animated: true)
var info = notification.userInfo!
var keyboardFrame: CGRect = (info[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue()
// handle your layout according to frame
}
I am aware of the keyboardWillShow and the keyboardWillHide events by:
override public func viewWillAppear(animated: Bool) {
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillHide:", name: UIKeyboardWillHideNotification, object: nil)
}
But with the new keyboards in iOS8 the keyboard is able to change without dismissing the keyboard and I was wondering how to call a function on keyboard size change. Anyone know? Thanks.
Edit: It is now calling on frame change but using this code:
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
NSLog("\(keyboardSize.height)")
}
It returns the old keyboard height for example when the frame changes to "224.0" it returns "253.0" as if the height has not updated by time the code is called, and when it goes have to "253.0" it returns the old height again which is "224.0"
Edit 2:
Instead of using "UIKeyboardFrameBeginUserInfoKey", I used "UIKeyboardFrameEndUserInfoKey" and it is now working.
You want UIKeyboardWillChangeFrameNotification and/or UIKeyboardDidChangeFrameNotification.
See the documentation of UIWindow for all of the keyboard related notifications.