Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift: registerForKeyboardNotifications from UIViewController extension

I am trying to clean up my code and create and extension to UIViewController to register and deregister my keyboard. In my utility file I have the following:

extension UIViewController {
     //Register the keyboard for notifications in  viewDidLoad
     func registerForKeyboardNotifications() {

         //Adding notifies on keyboard appearing
         NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(CreateEventVC.keyboardWasShown(_:)), name: UIKeyboardWillShowNotification, object: nil) 
         NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(CreateEventVC.keyboardWillBeHidden(_:)), name: UIKeyboardWillHideNotification, object: nil)

     }

     //Deregister the keyboard for notification in viewWillDisapper
     func deregisterFromKeyboardNotifications() {

         //Removing notifies on keyboard appearing
         NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
         NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
     }
}

What I need is the "CreateEventVC" in the selector to be the current Class.

Then I call it in my ViewController with the below:

override func viewDidLoad() {
         super.viewDidLoad()

         //Register Keyboard
         registerForKeyboardNotifications()
}

override func viewWillDisappear(animated: Bool) {

    deregisterFromKeyboardNotifications()

}

func keyboardWasShown(notification: NSNotification) {

    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {

        self.view.frame.origin.y -= keyboardSize.height

    }

}

func keyboardWillBeHidden(notification: NSNotification) {

    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {

        self.view.frame.origin.y += keyboardSize.height

    }

}

I have a feeling this isn't the best practice. Any help or advice will be great!

like image 934
Manstof Avatar asked Dec 11 '22 16:12

Manstof


1 Answers

I'm using UIViewController and UIScrollView extensions. If you're shifting your UIView you also can use this extension, but using UIScrollView is more convenient as it won't mess up with your view bounds and in case you need to add more textfields this approach is more generic.

extension UIViewController {
    func registerForKeyboardDidShowNotification(usingBlock block: ((NSNotification, CGSize) -> Void)? = nil) {
        NSNotificationCenter.defaultCenter().addObserverForName(UIKeyboardDidShowNotification, object: nil, queue: nil, usingBlock: { (notification) -> Void in
            let userInfo = notification.userInfo!
            guard let keyboardSize = userInfo[UIKeyboardFrameBeginUserInfoKey]?.CGRectValue.size else { fatalError("Can't grab the keyboard frame") }

            block?(notification, keyboardSize)
        })
    }

    func registerForKeyboardWillHideNotification(usingBlock block: (NSNotification -> Void)? = nil) {
        NSNotificationCenter.defaultCenter().addObserverForName(UIKeyboardWillHideNotification, object: nil, queue: nil, usingBlock: { (notification) -> Void in
            block?(notification)
        })
    }
}

so with this code, in your UIViewController's viewDidLoad() method you can register for keyboards notifications and add any additional functions in blocks. Like this:

override func viewDidLoad() {
    super.viewDidLoad()

    registerForKeyboardDidShowNotification { notif, size in
        self.view.frame.origin.y -= size.height
    }

    registerForKeyboardWillHideNotification { notif in
        self.view.frame.origin.y = 0
    }
}

EDIT: (For UIScrollView)

1)Can you animate the keyboard showing?

Actually keyboard appearance is always animated, so i think you're saying about the scrollview adjustments. Yes, you can. Below I posted updated UIViewController and UIScrollView extensions, which are handling that.

2) Do i need to deregister the keyboard?

Well, it's a good practice to avoid crashes in the future. For that you probably may prefer to add your notification in viewWillAppear and remove it in viewWillDisappear methods.

    NSNotificationCenter.defaultCenter().removeObserver(scrollView, name: UIKeyboardDidShowNotification, object: nil)
    NSNotificationCenter.defaultCenter().removeObserver(scrollView, name: UIKeyboardWillHideNotification, object: nil)

3) For the UIScrollView method would I put all my objects in a scroll view in the screen then just programmatically scroll the view when each UITextField becomes the first responder?

You don't scroll anything programmatically. What you need to do is to change the insets of UIScrollView and it will automatically make the firstResponder visible on the screen.

This is the code I use in my projects:

extension UIViewController {
    func registerForKeyboardDidShowNotification(scrollView: UIScrollView, usingBlock block: (CGSize? -> Void)? = nil) {
        NSNotificationCenter.defaultCenter().addObserverForName(UIKeyboardDidShowNotification, object: nil, queue: nil, usingBlock: { (notification) -> Void in
            let userInfo = notification.userInfo!
            let keyboardSize = userInfo[UIKeyboardFrameEndUserInfoKey]?.CGRectValue.size
            let contentInsets = UIEdgeInsetsMake(scrollView.contentInset.top, scrollView.contentInset.left, keyboardSize!.height, scrollView.contentInset.right)

            scrollView.setContentInsetAndScrollIndicatorInsets(contentInsets)
            block?(keyboardSize)
        })
    }

    func registerForKeyboardWillHideNotification(scrollView: UIScrollView, usingBlock block: (Void -> Void)? = nil) {
        NSNotificationCenter.defaultCenter().addObserverForName(UIKeyboardWillHideNotification, object: nil, queue: nil, usingBlock: { (notification) -> Void in
            let contentInsets = UIEdgeInsetsMake(scrollView.contentInset.top, scrollView.contentInset.left, 0, scrollView.contentInset.right)
            scrollView.setContentInsetAndScrollIndicatorInsets(contentInsets)
            block?()
        })
    }
}



extension UIScrollView {
    func setContentInsetAndScrollIndicatorInsets(edgeInsets: UIEdgeInsets) {
        self.contentInset = edgeInsets
        self.scrollIndicatorInsets = edgeInsets
    }
}

And in your viewWillAppear method, just add it like this:

registerForKeyboardWillShowNotification(scrollView)
registerForKeyboardWillHideNotification(scrollView)

And it will shrink the scrollview's contentInset whenever the keyboard appears and expand it whenever keyboard disappears.

like image 104
haik.ampardjian Avatar answered May 23 '23 11:05

haik.ampardjian