Getting keyboard size from userInfo in Swift

I have been trying to add some code to move my view up when the keyboard appears, however, I am having issues trying to translate the Objective-C examples into Swift. I have made some progress, but I am stuck on one particular line.

These are the two tutorials/questions I have been following:

How to move content of UIViewController upwards as Keypad appears using Swift http://www.ioscreator.com/tutorials/move-view-when-keyboard-appears

Here is the code I currently have:

override func viewWillAppear(animated: Bool) {
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillHide:", name: UIKeyboardWillHideNotification, object: nil)
}


override func viewWillDisappear(animated: Bool) {
NSNotificationCenter.defaultCenter().removeObserver(self)
}


func keyboardWillShow(notification: NSNotification) {
var keyboardSize = notification.userInfo(valueForKey(UIKeyboardFrameBeginUserInfoKey))
UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
let frame = self.budgetEntryView.frame
frame.origin.y = frame.origin.y - keyboardSize
self.budgetEntryView.frame = frame
}


func keyboardWillHide(notification: NSNotification) {
//
}

At the moment, I am getting an error on this line:

var keyboardSize = notification.userInfo(valueForKey(UIKeyboardFrameBeginUserInfoKey))

If someone could let me know what this line of code should be, I should manage to figure out the rest myself.

62654 次浏览

There are some problems in your line:

var keyboardSize = notification.userInfo(valueForKey(UIKeyboardFrameBeginUserInfoKey))
  • notification.userInfo returns an optional dictionary [NSObject : AnyObject]?, so it must be unwrapped before accessing its values.
  • The Objective-C NSDictionary is mapped to a Swift native Dictionary, so you must use the dictionary subscript syntax (dict[key]) to access the values.
  • The value must be cast to NSValue so that you can call CGRectValue on it.

All this can be achieved with a combination of optional assignment, optional chaining and optional casts:

if let userInfo = notification.userInfo {
if let keyboardSize = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
// ...
} else {
// no UIKeyboardFrameBeginUserInfoKey entry in userInfo
}
} else {
// no userInfo dictionary in notification
}

Or in one step:

if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
// ...
}

Update for Swift 3.0.1 (Xcode 8.1):

if let userInfo = notification.userInfo {
if let keyboardSize = userInfo[UIKeyboardFrameBeginUserInfoKey] as? CGRect {
let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
// ...
} else {
// no UIKeyboardFrameBeginUserInfoKey entry in userInfo
}
} else {
// no userInfo dictionary in notification
}

Or in one step:

if let keyboardSize = notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? CGRect {
let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
// ...
}

Update for Swift 5 (Xcode 11.6):

 guard let userInfo = notification.userInfo,
let keyboardSize = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return }

I recommend using keyboardFrameEndUserInfoKey instead of keyboardFrameBeginUserInfoKey since the keyboard changes the initial render height after the first display on older iOS devices.

You can use this one line for your line

var keyboardSize:CGSize = userInfo.objectForKey(UIKeyboardFrameBeginUserInfoKey)!.CGRectValue().size

This helped me : https://developer.apple.com/library/ios/samplecode/UICatalog/Listings/Swift_UICatalog_TextViewController_swift.html

let userInfo = notification.userInfo!


let animationDuration: NSTimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as NSNumber).doubleValue
let keyboardScreenBeginFrame = (userInfo[UIKeyboardFrameBeginUserInfoKey] as NSValue).CGRectValue()
let keyboardScreenEndFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as NSValue).CGRectValue()

For even less code consider looking at THIS

It was really helpful to me. You just have to include the view constraint in the view controller and using the two observers you added. Then just use the following methods (it is supposed here you move a tableView)

func keyboardWillShow(sender: NSNotification) {
if let userInfo = sender.userInfo {
if let keyboardHeight = userInfo[UIKeyboardFrameEndUserInfoKey]?.CGRectValue().size.height {
tableViewBottomConstraint.constant = keyboardHeight
UIView.animateWithDuration(0.25, animations: { () -> Void in
self.view.layoutIfNeeded()
})
}
}
}

and

func keyboardWillHide(sender: NSNotification) {
if let userInfo = sender.userInfo {
if let keyboardHeight = userInfo[UIKeyboardFrameEndUserInfoKey]?.CGRectValue().size.height {
tableViewBottomConstraint.constant = 0.0
UIView.animateWithDuration(0.25, animations: { () -> Void in self.view.layoutIfNeeded() })
}
} }

If you are using storyboard, rather than manipulating the view itself, you can take advantage of auto-layout.

(This is a cleaned up version of Nicholas's Answer)

Set up notification center to notify you of the appearance and disappearance of the keyboard:

override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name: UIKeyboardWillHideNotification, object: nil)


}

And make sure that you remove the observers when you don't need them any more:

override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: self.view.window)
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: self.view.window)
}

Inside storyboard, set the bottom constraint. Create an outlet of that constraint:

enter image description here

and set the constraint's constant property when the keyboard is shown or hidden:

func keyboardWillShow(notification: NSNotification) {
guard let keyboardHeight = (notification.userInfo! as NSDictionary).objectForKey(UIKeyboardFrameBeginUserInfoKey)?.CGRectValue.size.height else {
return
}
nameOfOutlet.constant = keyboardHeight
view.layoutIfNeeded()
}


func keyboardWillHide(notification: NSNotification) {
nameOfOutlet.constant = 0.0
view.layoutIfNeeded()
}

Now, whenever the keyboard appears or disappears, autolayout will will take care of everything.

Swift 2

func keyboardWasShown(notification:NSNotification) {
guard let info:[NSObject:AnyObject] = notification.userInfo,
let keyboardSize:CGSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue().size else { return }


let insets:UIEdgeInsets = UIEdgeInsetsMake(self.scrollView.contentInset.top, 0.0, keyboardSize.height, 0.0)


self.scrollView.contentInset = insets
self.scrollView.scrollIndicatorInsets = insets
}

Swift 3

func keyboardWasShown(notification:NSNotification) {
guard let info:[AnyHashable:Any] = notification.userInfo,
let keyboardSize:CGSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size else { return }


let insets:UIEdgeInsets = UIEdgeInsets(top: self.scrollView.contentInset.top, left: 0.0, bottom: keyboardSize.height, right: 0.0)


self.scrollView.contentInset = insets
self.scrollView.scrollIndicatorInsets = insets
}

Swift 3: UPDATE

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)


}


override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: self.view.window)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: self.view.window)
}

Swift 3.0

Here's an example of retrieving the keyboard size and using it to animate a view upward. In my case I am moving a UIView containing my UITextFields upward when a user begins typing so they can complete a form and still see the submit button at the bottom.

I added an outlet to the bottom space constraint of the view I wanted to animate and named it named myViewsBottomSpaceConstraint:

@IBOutlet weak var myViewsBottomSpaceConstraint: NSLayoutConstraint!

I then added the following code to my swift class:

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)


}


override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: self.view.window)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: self.view.window)
}


func keyboardWillShow(notification: NSNotification) {


let userInfo = notification.userInfo as! [String: NSObject] as NSDictionary
let keyboardFrame = userInfo.value(forKey: UIKeyboardFrameEndUserInfoKey) as! CGRect
let keyboardHeight = keyboardFrame.height
myViewsBottomSpaceConstraint.constant = keyboardHeight
view.layoutIfNeeded()
}


func keyboardWillHide(notification: NSNotification) {
myViewsBottomSpaceConstraint.constant = 0.0
view.layoutIfNeeded()
}

Details

  • Xcode Version 11.1 (11A1027), iOS 13, Swift 5

Solution

import UIKit


protocol KeyboardNotificationsDelegate: class {
func keyboardWillShow(notification: NSNotification)
func keyboardWillHide(notification: NSNotification)
func keyboardDidShow(notification: NSNotification)
func keyboardDidHide(notification: NSNotification)
}


extension KeyboardNotificationsDelegate {
func keyboardWillShow(notification: NSNotification) {}
func keyboardWillHide(notification: NSNotification) {}
func keyboardDidShow(notification: NSNotification) {}
func keyboardDidHide(notification: NSNotification) {}
}


class KeyboardNotifications {


fileprivate var _isEnabled: Bool
fileprivate var notifications: [KeyboardNotificationsType]
fileprivate weak var delegate: KeyboardNotificationsDelegate?


init(notifications: [KeyboardNotificationsType], delegate: KeyboardNotificationsDelegate) {
_isEnabled = false
self.notifications = notifications
self.delegate = delegate
}


deinit { if isEnabled { isEnabled = false } }
}


// MARK: - enums


extension KeyboardNotifications {


enum KeyboardNotificationsType {
case willShow, willHide, didShow, didHide


var selector: Selector {
switch self {
case .willShow: return #selector(keyboardWillShow(notification:))
case .willHide: return #selector(keyboardWillHide(notification:))
case .didShow: return #selector(keyboardDidShow(notification:))
case .didHide: return #selector(keyboardDidHide(notification:))
}
}


var notificationName: NSNotification.Name {
switch self {
case .willShow: return UIResponder.keyboardWillShowNotification
case .willHide: return UIResponder.keyboardWillHideNotification
case .didShow: return UIResponder.keyboardDidShowNotification
case .didHide: return UIResponder.keyboardDidHideNotification
}
}
}
}


// MARK: - isEnabled


extension KeyboardNotifications {


private func addObserver(type: KeyboardNotificationsType) {
NotificationCenter.default.addObserver(self, selector: type.selector, name: type.notificationName, object: nil)
}


var isEnabled: Bool {
set {
if newValue {
for notificaton in notifications { addObserver(type: notificaton) }
} else {
NotificationCenter.default.removeObserver(self)
}
_isEnabled = newValue
}


get { return _isEnabled }
}


}


// MARK: - Notification functions


extension KeyboardNotifications {


@objc func keyboardWillShow(notification: NSNotification) {
delegate?.keyboardWillShow(notification: notification)
}


@objc func keyboardWillHide(notification: NSNotification) {
delegate?.keyboardWillHide(notification: notification)
}


@objc func keyboardDidShow(notification: NSNotification) {
delegate?.keyboardDidShow(notification: notification)
}


@objc func keyboardDidHide(notification: NSNotification) {
delegate?.keyboardDidHide(notification: notification)
}
}

Usage

class ViewController: UIViewController {


private lazy var keyboardNotifications: KeyboardNotifications! = {
return KeyboardNotifications(notifications: [.willShow, .willHide, .didShow, .didHide], delegate: self)
}()


override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
keyboardNotifications.isEnabled = true
}


override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
keyboardNotifications.isEnabled = false
}
}


extension ViewController: KeyboardNotificationsDelegate {


// If you don't need this func you can remove it
func keyboardWillShow(notification: NSNotification) {
print("keyboardWillShow")
guard   let userInfo = notification.userInfo as? [String: NSObject],
let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return }
print("keyboardFrame: \(keyboardFrame)")
}


// If you don't need this func you can remove it
func keyboardWillHide(notification: NSNotification) { print("keyboardWillHide") }


// If you don't need this func you can remove it
func keyboardDidShow(notification: NSNotification) { print("keyboardDidShow") }


// If you don't need this func you can remove it
func keyboardDidHide(notification: NSNotification) { print("keyboardDidHide") }
}

Full Sample

import UIKit


class ViewController: UIViewController {


private lazy var keyboardNotifications: KeyboardNotifications! = {
return KeyboardNotifications(notifications: [.willShow, .willHide, .didShow, .didHide], delegate: self)
}()


override func viewDidLoad() {
super.viewDidLoad()


let textField = UITextField(frame: CGRect(x: 40, y: 40, width: 200, height: 30))
textField.borderStyle = .roundedRect
view.addSubview(textField)


let gesture = UITapGestureRecognizer(target: view, action: #selector(UIView.endEditing(_:)))
view.addGestureRecognizer(gesture)
}


override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
keyboardNotifications.isEnabled = true
}


override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
keyboardNotifications.isEnabled = false
}
}


extension ViewController: KeyboardNotificationsDelegate {


// If you don't need this func you can remove it
func keyboardWillShow(notification: NSNotification) {
print("keyboardWillShow")
guard   let userInfo = notification.userInfo as? [String: NSObject],
let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return }
print("keyboardFrame: \(keyboardFrame)")
}


// If you don't need this func you can remove it
func keyboardWillHide(notification: NSNotification) { print("keyboardWillHide") }


// If you don't need this func you can remove it
func keyboardDidShow(notification: NSNotification) { print("keyboardDidShow") }


// If you don't need this func you can remove it
func keyboardDidHide(notification: NSNotification) { print("keyboardDidHide") }
}

Result

enter image description here

Log

enter image description here

For xamarin, you can use c#6

private void KeyboardWillChangeFrame(NSNotification notification)
{
var keyboardSize = notification.UserInfo.ValueForKey(UIKeyboard.FrameEndUserInfoKey) as NSValue;
if (keyboardSize != null)
{
var rect= keyboardSize.CGRectValue;
//do your stuff here
}
}

c#7

  private void KeyboardWillChangeFrame(NSNotification notification)
{
if (!(notification.UserInfo.ValueForKey(UIKeyboard.FrameEndUserInfoKey) is NSValue keyboardSize)) return;
var rect= keyboardSize.CGRectValue;
}

in Swift 4.2 you can use UIResponder.keyboardFrameEndUserInfoKey

guard let userInfo = notification.userInfo , let keyboardFrame:CGRect = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect  else { return  }```

Swift - Keyboard Height From keyboardWillShowNotification

You can grow or shrink a constraint, or any other value, to the size of the keyboard using data from the keyboard Will/did Show/hide Notifications.

With a Layout Constraint

This minimal code registers for notification that the keyboard will show and updates a constraint based on its size.

@IBOutlet weak var keyboardConstraint: NSLayoutConstraint!
let keyboardConstraintMargin:CGFloat = 20


override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: nil) { (notification) in
if let keyboardSize = notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? CGRect {
self.keyboardConstraint.constant = keyboardSize.height + self.keyboardConstraintMargin
}
}
NotificationCenter.default.addObserver(forName: UIResponder.keyboardDidHideNotification, object: nil, queue: nil) { (notification) in
self.keyboardConstraint.constant = self.keyboardConstraintMargin
}
}

With a ScrollView

In the same way this updates the content inset of a scroll view based on the keyboard's size.

@IBOutlet weak var scrollView: UIScrollView!


override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: nil) { (notification) in
if let keyboardSize = notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? CGRect {
let insets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
self.scrollView.contentInset = insets
self.scrollView.scrollIndicatorInsets = insets
}
}
NotificationCenter.default.addObserver(forName: UIResponder.keyboardDidHideNotification, object: nil, queue: nil) { (notification) in
let insets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
self.scrollView.contentInset = insets
self.scrollView.scrollIndicatorInsets = insets
}
}