Constrain View Above Keyboard with Animation January 30, 2017

Often you will allow user input with the keyboard and need the content of the view above to modify its constraints to sit above the keyboard and not be hidden by it. To accomplish this, we will add an observer for UIKeyboardWillChangeFrame events with NotificationCenter, and then adjust the bottomAnchor constraint of our view with an animation curve matching the animation of the keyboard. The result will be a smooth transition of the constraints on the view that will remain above the keyboard in all cases.

First we declare a view and an array of NSLayoutConstraint.

            
      let tableView = UITableView()
      var tableViewConstraints: [NSLayoutConstraint]?
             
          

In viewDidLoad we activate constraints and add our NotificationCenter observer.

            
      override func viewDidLoad() {
        super.viewDidLoad()

        tableViewConstraints = [
            tableView.view.topAnchor.constraint(equalTo: self.view.topAnchor),
            tableView.view.bottomAnchor.constraint(equalTo: self.view.bottomAnchor), // the constraint we will be adjusting
            tableView.view.leftAnchor.constraint(equalTo: self.view.leftAnchor),
            tableView.view.rightAnchor.constraint(equalTo: self.view.rightAnchor),
        ]
        
        // and activate them
        NSLayoutConstraint.activate(tableViewConstraints!)
        
        // add notification to capture keyboard changing frame event
        NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardNotification(notification:)), name:NSNotification.Name.UIKeyboardWillChangeFrame, object: nil);
      }
      
          

When NotificationCenter notifies that the keyboard's frame is changing we get the destination frame of the keyboard. When UIView animates, the frame instantanously moves to the final frame, even before the view gets there. The view's location as it animates does not equal its frame until it reaches its destination.

Therefore at the beginning of the keyboard animation we already know the final frame and animationCurve the keyboard will take, so we constrain our tableView to the curve of that animation.

            
      func keyboardNotification(notification: NSNotification) {
        if let userInfo = notification.userInfo {
            // get destination of keyboard
            let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
            // get duration of animation
            let duration:TimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
            // get animation curve
            let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
            let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIViewAnimationOptions.curveEaseInOut.rawValue
            let animationCurve:UIViewAnimationOptions = UIViewAnimationOptions(rawValue: animationCurveRaw)r
            // check if keyboard frame is off the screen
            if (endFrame?.origin.y)! >= UIScreen.main.bounds.size.height {
                // if yes set the bottom constraint to 0.0
                self.tableViewConstraints?[1].constant = 0.0
            } else {
                // if no (therefore keyboard is visible) 
                // set the bottomAnchor's constant to the inverse of the frame's height
                self.tableViewConstraints?[1].constant = (endFrame?.size.height)! * -1
            }
            // perform the animation
            UIView.animate(withDuration: duration,
                           delay: TimeInterval(0),
                           options: animationCurve,
                           animations: { self.view.layoutIfNeeded()},
                           completion: nil)
        }
      }

          

To clean up we deinit the observer.

            
      deinit {
         NotificationCenter.default.removeObserver(self)
      }
              
          

That's it! Have fun and #CompileSwift.