I have a scrollView that i want to scroll up when the keyboard is shown.
I have a crash with this error when the keyboard show :
2014-09-29 14:48:50.738 swrd[1563:472888] -[swrd.EditPhotoViewController keyboardWasShown]: unrecognized selector sent to instance 0x14ed36640
Here is my code, what's wrong ?:
func registerForKeyboardNotifications ()-> Void {
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWasShown", name: UIKeyboardDidShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillBeHidden", name: UIKeyboardWillHideNotification, object: nil)
}
func deregisterFromKeyboardNotifications () -> Void {
let center: NSNotificationCenter = NSNotificationCenter.defaultCenter()
center.removeObserver(self, name: UIKeyboardDidHideNotification, object: nil)
center.removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
}
func keyboardWasShown (notification: NSNotification) {
let info : NSDictionary = notification.userInfo!
let keyboardSize = info.objectForKey(UIKeyboardFrameBeginUserInfoKey)?.frame
let insets: UIEdgeInsets = UIEdgeInsetsMake(self.scrollView.contentInset.top, 0, keyboardSize!.height, 0)
self.scrollView.contentInset = insets
self.scrollView.scrollIndicatorInsets = insets
self.scrollView.contentOffset = CGPointMake(self.scrollView.contentOffset.x, self.scrollView.contentOffset.y + keyboardSize!.height)
}
func keyboardWillBeHidden (notification: NSNotification) {
let info : NSDictionary = notification.userInfo!
let keyboardSize = info.objectForKey(UIKeyboardFrameBeginUserInfoKey)?.frame
let insets: UIEdgeInsets = UIEdgeInsetsMake(self.scrollView.contentInset.top, 0, keyboardSize!.height, 0)
self.scrollView.contentInset = insets
self.scrollView.scrollIndicatorInsets = insets
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
self.registerForKeyboardNotifications()
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(true)
self.deregisterFromKeyboardNotifications()
}
In your code, keyboardWasShown and keyboardWasHidden each take an argument, the NSNotification. You need to terminate your selectors in addObserver with a colon so that it gets passed. I.e., keyboardWasShown and keyboardWasShown: are different selectors.
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWasShown:", name: UIKeyboardDidShowNotification, object: nil)
See a self-contained solution below:
private func startObservingKeyboardEvents() {
NSNotificationCenter.defaultCenter().addObserver(self,
selector:Selector("keyboardWillShow:"),
name:UIKeyboardWillShowNotification,
object:nil)
NSNotificationCenter.defaultCenter().addObserver(self,
selector:Selector("keyboardWillHide:"),
name:UIKeyboardWillHideNotification,
object:nil)
}
private func stopObservingKeyboardEvents() {
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
}
func keyboardWillShow(notification: NSNotification) {
if let userInfo = notification.userInfo {
if let keyboardSize: CGSize = userInfo[UIKeyboardFrameEndUserInfoKey]?.CGRectValue().size {
let contentInset = UIEdgeInsetsMake(0.0, 0.0, keyboardSize.height, 0.0);
}
}
}
func keyboardWillHide(notification: NSNotification) {
let contentInset = UIEdgeInsetsZero;
}
Use the contentInset variable to adjust the content insets.
In my case, was need some changes in the codes above:
1 - First you need put a Scrollview in your view, and set constraints like this:
It's important your scrollView be grater than view.
2 - Put your TextField's and other components you need.
3 - Link ScrollView to the ViewController with #IBOutlet
4 - Add the Delegate to ScrollView:
class ViewController: UIViewController, UITextFieldDelegate, UIScrollViewDelegate {
...
5 - Add the Observer's:
override func viewWillAppear(animated: Bool) {
self.startKeyboardObserver()
}
override func viewWillDisappear(animated: Bool) {
self.stopKeyboardObserver()
}
private func startKeyboardObserver(){
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil) //WillShow and not Did ;) The View will run animated and smooth
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillHide:", name: UIKeyboardWillHideNotification, object: nil)
}
private func stopKeyboardObserver() {
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
}
6 - Add The code to scroll, calculating KeyboardSize.
func keyboardWillShow(notification: NSNotification) {
if let userInfo = notification.userInfo {
if let keyboardSize: CGSize = userInfo[UIKeyboardFrameEndUserInfoKey]?.CGRectValue().size {
let contentInset = UIEdgeInsetsMake(0.0, 0.0, keyboardSize.height, 0.0);
self.scrollView.contentInset = contentInset
self.scrollView.scrollIndicatorInsets = contentInset
self.scrollView.contentOffset = CGPointMake(self.scrollView.contentOffset.x, 0 + keyboardSize.height) //set zero instead self.scrollView.contentOffset.y
}
}
}
func keyboardWillHide(notification: NSNotification) {
if let userInfo = notification.userInfo {
if let keyboardSize: CGSize = userInfo[UIKeyboardFrameEndUserInfoKey]?.CGRectValue().size {
let contentInset = UIEdgeInsetsZero;
self.scrollView.contentInset = contentInset
self.scrollView.scrollIndicatorInsets = contentInset
self.scrollView.contentOffset = CGPointMake(self.scrollView.contentOffset.x, self.scrollView.contentOffset.y)
}
}
}
Related
I have a Uiviewcontroller, in this viewcontroller I put a uiview called categUIV. The categUIV contains uitexfields and a button. The idea is to move the hole categUIV up when the keyboard is up.
I use the code below
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name: UIKeyboardWillHideNotification, object: nil)
}
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
self.categUIV.frame.origin.y -= keyboardSize.height
}
}
func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
self.categUIV.frame.origin.y += keyboardSize.height
}
}
Now when I click to a uitextfied the keyboard pops up and categUIV pops up correctly, the problem is when I start typing the categUIV go back to his initial position by itself.
I don’t know why but i wonder is that maybe because of the constraints of categUIV?
The problem is with frame-layout things don't work as expected , Try autolayout , hook the bottom constraint of the categUIV as IBOutlet and do this
#objc func handleKeyboardDidShow (notification: NSNotification)
{
let keyboardRectAsObject = notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue
var keyboardRect = CGRect.zero
keyboardRectAsObject.getValue(&keyboardRect)
self.categUIVBotcon.constant = -1 * keyboardRect.height
UIView.animate(withDuration: 0.5,animations: {
self.view.layoutIfNeeded()
})
}
#objc func handleKeyboardWillHide(notification: NSNotification)
{
self.categUIVBotcon.constant = 0
UIView.animate(withDuration: 0.5,animations: {
self.view.layoutIfNeeded()
})
}
To make this work, you need to put your view into a scroll view so that your hierarchy looks like this:
- View Controller
- View
- Scroll View
- Content View
- categUIV
You must also connect the scroll view and the categUIV to your code:
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var categUIV: UIView!
You can then register your keyboard observers:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWasShown(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillBeHidden(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
With the following functions:
func keyboardWasShown(notification: NSNotification) {
self.keyboardSize = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue.size
self.scrollView.isScrollEnabled = true
let contentInsets = UIEdgeInsetsMake(0.0, 0.0, self.keyboardSize.height, 0.0)
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
var rect = self.view.frame
rect.size.height -= self.keyboardSize.height
if !rect.contains(self.categUIV.frame.origin) {
self.scrollView.scrollRectToVisible(self.categUIV.frame, animated: true)
}
}
func keyboardWillBeHidden(notification: NSNotification) {
let keyboardSize = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue.size
let contentInsets = UIEdgeInsetsMake(0.0, 0.0, -keyboardSize.height, 0.0)
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
self.scrollView.isScrollEnabled = false
}
How do you adjust your scrollview to compensate for a keyboard vertically? Read on...
Yes I know this is some basic info, but I randomly noticed today that all of the answers I saw about this topic are all over the place with info, versions and/or use bangs all over the place... but nothing solid for Swift 3+.
Swift 4.2:
Substitute scrollView for UITableView, UICollectionView, etc.
let scrollView = UIScrollView()
Add observers.
override open func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
}
Add some functions to listen for the notifications:
#objc func keyboardWillHide(notification: Notification) {
let contentInsets = UIEdgeInsets.zero
scrollView.contentInset = contentInsets
scrollView.scrollIndicatorInsets = contentInsets
}
#objc func keyboardWillShow(notification: Notification) {
guard let keyboardFrame: CGRect = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else { return }
scrollView.contentInset.bottom = keyboardFrame.height
}
Worth noting is that if your deployment target is iOS 9 or greater, you don't need to remove the observer anymore. Check the NotificationCenter docs for more info.
deinit {
NotificationCenter.default.removeObserver(self)
}
------------------------------------------------
Swift 3:
let scrollView = UIScrollView()
Add observers.
override open func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(noti:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
NSNotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(noti:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
}
Add some functions to listen for the notifications:
func keyboardWillHide(noti: Notification) {
let contentInsets = UIEdgeInsets.zero
scrollView.contentInset = contentInsets
scrollView.scrollIndicatorInsets = contentInsets
}
func keyboardWillShow(noti: Notification) {
guard let userInfo = noti.userInfo else { return }
guard var keyboardFrame: CGRect = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue else { return }
keyboardFrame = self.view.convert(keyboardFrame, from: nil)
var contentInset:UIEdgeInsets = scrollView.contentInset
contentInset.bottom = keyboardFrame.size.height
scrollView.contentInset = contentInset
}
Worth noting is that if your deployment target is iOS 9 or greater, you don't need to remove the observer anymore. Check the NotificationCenter docs for more info.
deinit {
NotificationCenter.default.removeObserver(self)
}
A modification to make it work on iOS 11 is to use UIKeyboardFrameEndUserInfoKey rather than UIKeyboardFrameBeginUserInfoKey. Just a simplified approach to #crewshin's solution:
#objc func keyboardWillShow(_ notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
scrollView.contentInset.bottom = keyboardSize.height
}
}
#objc func keyboardWillHide(_ notification: NSNotification) {
scrollView.contentInset.bottom = 0
}
I am showing a popup in my view controller and the popup contains a tableview and a textfield so when I am clicking on textfield , my popup height remains same . so I want to reduce the height of my popup view when clicked on textfield. can anyone please help me out in this?
Just add two this line in viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.keyboardWasShown), name: UIKeyboardDidShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.keyboardWillBeHidden), name: UIKeyboardWillHideNotification, object: nil)
// keyboard delegates method implement
func keyboardWasShown(aNotification: NSNotification) {
print("Keyboard is active.")
// write your code that changes pop up frame.
}
func keyboardWillBeHidden(aNotification: NSNotification) {
print("Keyboard is hidden")
// write your code that changes pop up default frame.
}
You try this code add notification add notification
NotificationCenter.default.addObserver(self, selector: #selector(ClassName.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(ClassName.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
Table automatic manage when keyboard appear
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
UIView.animate(withDuration: 0.5) {
var contentInsets:UIEdgeInsets
if (UIInterfaceOrientationIsPortrait(UIApplication.shared.statusBarOrientation)) {
contentInsets = UIEdgeInsetsMake(0.0, 0.0, (keyboardSize.height - (self.tabBarController?.tabBar.frame.size.height)!), 0.0);
} else {
contentInsets = UIEdgeInsetsMake(0.0, 0.0, (keyboardSize.width - (self.tabBarController?.tabBar.frame.size.width)!), 0.0);
}
self.tableView.contentInset = contentInsets
self.tableView.scrollIndicatorInsets = self.tableView.contentInset
}
}
}
func keyboardWillHide(notification: NSNotification) {
UIView.animate(withDuration: 0.5) {
let contentInset:UIEdgeInsets = UIEdgeInsets.zero
self.tableView.contentInset = contentInset
}
}
I have a UIViewController with a container View. The container view's child is a static tableView. The last cell of the tableView has a text field. While working with only UITableViewController, tableViewController handled the movement of the tableView when text field was selected. Now, even though the keyboard appears, the tableView is not adjusting itself. Any solution?
You can use keyboard notification for scrolling the tableview up
// Keyboard
func registerForKeyboardNotifications() {
NSNotificationCenter.defaultCenter().addObserver(self,
selector: #selector(keyboardWillShow),
name: UIKeyboardWillShowNotification,
object: nil)
NSNotificationCenter.defaultCenter().addObserver(self,
selector: #selector(keyboardWillHide),
name: UIKeyboardWillHideNotification,
object: nil)
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
}
func keyboardWillShow(notification: NSNotification) {
if let userInfo = notification.userInfo {
if let keyboardSize = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
let keyboardHeight = keyboardSize.height
let contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardHeight, 0.0)
tableView.contentInset = contentInsets
tableView.scrollIndicatorInsets = contentInsets
}
}
}
func keyboardWillHide(notification: NSNotification) {
tableView.contentInset = UIEdgeInsetsZero
tableView.scrollIndicatorInsets = UIEdgeInsetsZero
}
them you call on viewDidLoad function
override func viewDidLoad() {
super.viewDidLoad()
registerForKeyboardNotifications()
}
I have tableview with two textfields and one Image pickerView button.
If I write something, my textfields are upping, and it's okay.
But, if I choose photo from my imagePicker view and try to write something, I don't see my textfields. Please, help.
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name: UIKeyboardDidShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name: UIKeyboardDidHideNotification, object: nil)
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func keyboardWillShow(notification: NSNotification) {
if let userInfo = notification.userInfo {
if let keyboardHeight = userInfo[UIKeyboardFrameEndUserInfoKey]?.CGRectValue.size.height {
tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardHeight, 0)
}
}
}
func keyboardWillHide(notification: NSNotification) {
if let userInfo = notification.userInfo {
tableView.contentInset = UIEdgeInsetsMake(50, 0, 0, 0)
}
}