Code Monkey home page Code Monkey logo

keyboard-manager-swift's Introduction

Keyboard Manager

This repo solves a problem I came across in using notifications to manage the iOS Keyboard.

Managing the Keyboard

When I first started managing the keyboard I would use separate Notifications in each ViewController.

Notification Method (Using Notification):

class ViewController: UIViewController {
  
  override func viewDidLoad() {
    super.viewDidLoad()
      NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardNotification), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
  }
  
  func keyboardNotification(notification: Notification) {
    guard let userInfo = notification.userInfo else { return }
    
    guard let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else { return }
    let duration: TimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
    let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
    let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIViewAnimationOptions.curveEaseOut.rawValue
    let animationCurve: UIViewAnimationOptions = UIViewAnimationOptions(rawValue: animationCurveRaw)
    
    if endFrame.origin.y >= UIScreen.main.bounds.size.height {
      lowerViewBottomConstraint.constant = 0
    } else {
      lowerViewBottomConstraint.constant = endFrame?.size.height ?? 0.0
    }

    view.animateConstraint(withDuration: duration, delay: TimeInterval(0), options: animationCurve, completion: nil)
  }
}

My problem was that I found myself writing this code again and again for every single ViewController. After experimenting a bit I found using a Singleton + Delegate pattern allowed me to reuse a bunch of code and organize all of the Keyboard Management in a single place!

Singleton + Delegate Method:

protocol KeyboardManagerDelegate: class {
  func keyboardWillChangeFrame(endFrame: CGRect?, duration: TimeInterval, animationCurve: UIViewAnimationOptions)
}

class KeyboardManager {
  
  static let sharedInstance = KeyboardManager()
  
  weak var delegate: KeyboardManagerDelegate?
  
  init() {
    NotificationCenter.default.addObserver(self, selector: #selector(KeyboardManager.keyboardWillChangeFrameNotification), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
  }
  
  @objc func keyboardWillChangeFrameNotification(notification: Notification) {
    guard let userInfo = notification.userInfo else { return }
    
    let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
    let duration: TimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
    let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
    let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIViewAnimationOptions.curveEaseOut.rawValue
    let animationCurve: UIViewAnimationOptions = UIViewAnimationOptions(rawValue: animationCurveRaw)
    
    delegate?.keyboardWillChangeFrame(endFrame: endFrame, duration: duration, animationCurve: animationCurve)
  }
}

Now when I want to manage the keyboard from a ViewController all I need to do is set the delegate to that ViewController and implement any delegate methods.

class ViewController: UIViewController {
  override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    KeyboardManager.sharedInstance.delegate = self
  }
}

// MARK: - Keyboard Manager

extension ViewController: KeyboardManagerDelegate {
  func keyboardWillChangeFrame(endFrame: CGRect?, duration: TimeInterval, animationCurve: UIViewAnimationOptions) {
    guard let endFrame = endFrame else { return }
    
    if endFrame.origin.y >= UIScreen.main.bounds.size.height {
      lowerViewBottomConstraint.constant = 0
    } else {
      lowerViewBottomConstraint.constant = (endFrame?.size.height ?? 0.0)
    }
    view.animateConstraint(withDuration: duration, delay: TimeInterval(0), options: animationCurve, completion: nil)
  }
}

This method is very customizable too! ๐ŸŽ‰๐Ÿ’ฏ Say we want to add functionality for NSNotification.Name.UIKeyboardWillHide. This is as easy as adding a method to our KeyboardManagerDelegate.

KeyboardManagerDelegate with NSNotification.Name.UIKeyboardWillHide:

protocol KeyboardManagerDelegate: class {
  func keyboardWillChangeFrame(endFrame: CGRect?, duration: TimeInterval, animationCurve: UIViewAnimationOptions)
  func keyboardWillHide(userInfo: [AnyHashable: Any])
}

class KeyboardManager {
  
  static let sharedInstance = KeyboardManager()
  
  weak var delegate: KeyboardManagerDelegate?
  
  init() {
    NotificationCenter.default.addObserver(self, selector: #selector(KeyboardManager.keyboardWillChangeFrameNotification), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(KeyboardManager.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
  }
  
  @objc func keyboardWillChangeFrameNotification(notification: Notification) {
    guard let userInfo = notification.userInfo else { return }
    
    let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
    let duration: TimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
    let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
    let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIViewAnimationOptions.curveEaseOut.rawValue
    let animationCurve: UIViewAnimationOptions = UIViewAnimationOptions(rawValue: animationCurveRaw)
    
    delegate?.keyboardWillChangeFrame(endFrame: endFrame, duration: duration, animationCurve: animationCurve)
  }
  
  @objc func keyboardWillHide(notification: Notification) {
    guard let userInfo = notification.userInfo else { return }
    delegate?.keyboardWillHide(userInfo: userInfo)
  }
}

Say we only want to implement func keyboardWillHide(userInfo: [AnyHashable: Any]) in one UIViewController. We can also make this method optional.

typealias KeyboardManagerDelegate = KeyboardManagerModel & KeyboardManagerConfigureable

protocol KeyboardManagerModel: class {
  func keyboardWillChangeFrame(endFrame: CGRect?, duration: TimeInterval, animationCurve: UIViewAnimationOptions)
}

@objc protocol KeyboardManagerConfigureable {
  @objc optional func keyboardWillHide(userInfo: [AnyHashable: Any])
}

Note: this pattern helps avoid overuse of @objc. See HERE for more details!

In summary, I've found using a Singleton + Delegate to manage the keyboard is both more efficient and easier to use than using Notifications.

keyboard-manager-swift's People

Contributors

richard-ash avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.