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...
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
I create multiple UITextFields in my storyboard. Some text field are edited using keyboard while others using picker view. In my view controller, for the fields using pickerView, I created separate picker views for each of the text fields. For eg.
aTextField.inputView = aPickerView
Now for the text fields which are at the bottom of the screen, when I tap on one of them, picker view corresponding to that text field opens at the bottom and it hides the textfield. I want to shift the currently selected text field up when the picker view opens.
In case of keyboard, shifting of views can be done by responding to UIResponder.keyboardWillShowNotification. How to do this in case of picker view?
One solution that I can think of is observing keyboard height. When you press the picker view that has to show up the bottom textField, use keyboard height and some extra points to move it over the keyboard. Here are some helper methods to achieve that:
override func viewDidLoad() {
super.viewDidLoad()
// Add keyboard observers
addObservers()
}
private func addObservers() {
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}
// keyboard will be active
#objc private func keyboardWillShow(notification: NSNotification) {
// Get keyboard Size
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
// update constraints / footerview height with keyboard height "keyboardSize.height"
}
}
// keyboard will dismiss
#objc private func keyboardWillHide(notification: NSNotification) {
if (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue != nil {
// update constraints / footerview height when keyboard is hidden,
}
}
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 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.
I have two views with a label on one of them. On the second view, there is a button. What I want to achieve here is to be able to press the button and it updates the label on the first view. How do I do that? I can't access the IBOutlet from the second view. Is there something that I have to do to the IBOutlet to make it public etc?
You can use NSNotificationCenter for that.
First of all in your viewDidLoad method add this code in your firstViewController class:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "refreshlbl:", name: "refresh", object: nil)
which will addObserver when your app loads.
And add this helper method in same viewController.
func refreshlbl(notification: NSNotification) {
lbl.text = "Updated by second View" //Update your label here.
}
After that in your secondViewController add this code when you dismiss your view:
#IBAction func back(sender: AnyObject) {
NSNotificationCenter.defaultCenter().postNotificationName("refresh", object: nil, userInfo: nil)
self.dismissViewControllerAnimated(true, completion: nil)
}
Now when you press back button from secondView then refreshlbl method will call from firstView.
use custom delegate method create a delegate in second view and access that view delegate function in first view. or use NSNotification or use NSUserdefaults