Advertise here

Advertise here

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Sliding your views up to make room for the keyboard using auto-layout

Duncan CDuncan C Posts: 9,116Tutorial Authors, Registered Users @ @ @ @ @ @ @
edited January 2014 in iPhone SDK Tutorials
When it comes to AutoLayout in iOS 6 and iOS 7, everything you know is wrong.

Using struts and springs, you can just shift your views's centers to make room for the keyboard. No such luck using AutoLayout. There you have to manipulate constraints.

The trick is put everything in your VC that you want to shift up (usually all the contents of your VC except the navigation bar) in a container view, with a top constraint tied to the top layout guide and a bottom constraint tied to the bottom layout guide.

You then add IBOutlets to those constraints so you can change them in code.

Then, you add handlers for the will show keyboard notification and the will hide keyboard notification. You have to do a bunch of math to figure out how much to shift things, and finally you grab the top constraint and subtract from it's "constant" value, and add the same amount to the bottom constraint.

The code I use looks like this:
  //Set up a notification handler to shift the content view up to make room for the keyboard if the current text field
  //Will be covered by the keyboard.
  showKeyboardNotificaiton = [[NSNotificationCenter defaultCenter] addObserverForName: UIKeyboardWillShowNotification
                                                                               object: nil
                                                                                queue: nil
                                                                           usingBlock: ^(NSNotification *note)
                                CGRect keyboardFrame;
                                NSDictionary* userInfo = note.userInfo;
                                //keyboardSlideDuration is an instance variable so we can keep it around to use in the "dismiss keyboard" animation.
                                keyboardSlideDuration = [[userInfo objectForKey: UIKeyboardAnimationDurationUserInfoKey] floatValue];

                                //Get the animation curve from the user info and convert it
                                //from a UIViewAnimationCurve value to a UIViewAnimationOptions value
                                //keyboardAnimationCurve is an instance variable so we can keep it around to use in the "dismiss keyboard" animation.
                                 keyboardAnimationCurve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue]<<16;

                                //Get the size of the keyboard.
                                keyboardFrame = [[userInfo objectForKey: UIKeyboardFrameBeginUserInfoKey] CGRectValue];
                                UIInterfaceOrientation theStatusBarOrientation = [[UIApplication sharedApplication] statusBarOrientation];
                                CGFloat keyboardHeight;
                                //The keyboard frame will not refelect the current orietnation. height = width if we're in landscape...
                                if UIInterfaceOrientationIsLandscape(theStatusBarOrientation)
                                  keyboardHeight = keyboardFrame.size.width;
                                  keyboardHeight = keyboardFrame.size.height;
                                //Get the bounds of the current text field.
                                CGRect fieldFrame = textFieldToEdit.bounds;
                                //Convert the field's bounds to the coordinates of the VC's content view.
                                fieldFrame = [self.view convertRect: fieldFrame fromView: textFieldToEdit];
                                CGRect contentFrame = self.view.frame;
                                //Calculate the Y position of the bottom of the input field.
                                CGFloat fieldBottom = fieldFrame.origin.y + fieldFrame.size.height;
                                keyboardShiftAmount= 0;
                                //If the bottom of the input field is going to be covered by the keyboard...
                                if (contentFrame.size.height + contentFrame.origin.y - fieldBottom <keyboardHeight)
                                  //Figure out how much to shift the container view to expose the input field (plus 5 pixels of "breathing room")
                                  keyboardShiftAmount = keyboardHeight - (contentFrame.size.height + contentFrame.origin.y - fieldBottom)+5;
                                  //keyboardShiftAmount is an instance variable so we can use it to shift the container view back again when the keyboard disappears.
                                  //Adjust the top and bottom constraints for the container view
                                  containerTopConstraint.constant -= keyboardShiftAmount;
                                  containerBottomConstraint.constant += keyboardShiftAmount;
                                  //animate the change to the view constraint using
                                  //the duration and animation curve specified in the keyboard notification.
                                  [UIView animateWithDuration: keyboardSlideDuration
                                                        delay: 0
                                                      options: keyboardAnimationCurve
                                                     [containerView layoutIfNeeded];
                                   completion: nil

  //Set up another notification handler to move the content view back down as the keyboard is dismissed.
  hideKeyboardNotificaiton = [[NSNotificationCenter defaultCenter] addObserverForName: UIKeyboardWillHideNotification
                                                                               object: nil
                                                                                queue: nil
                                                                           usingBlock: ^(NSNotification *note)
                                if (keyboardShiftAmount != 0)
                                  [UIView animateWithDuration: keyboardSlideDuration
                                                        delay: 0
                                                      options: keyboardAnimationCurve
                                     //Rervese the changes to the container view's top and bottom constraints
                                     //from the show keyboard animation above
                                     containerBottomConstraint.constant -= keyboardShiftAmount;
                                     containerTopConstraint.constant += keyboardShiftAmount;
                                     [self.view setNeedsUpdateConstraints];
                                     [containerView layoutIfNeeded];
                                                   completion: nil

That code uses a number of instance variables to keep track of things, and outlets to the top and bottom constraints (as mentioned above)

It's easier to show a working project using this technique then to try to explain everything.

You can see it working in a project I put up on github today.

UIImageView frame animation with cross-fading.

The project's main purpose is to illustrate UIImageView animation that supports cross-fading, but the keyboard shifting is a side-benefit. I figured it could use it's own tutorial topic, since it is NOT easy to figure out how to do this.
Post edited by Duncan C on
Duncan C

Animated GIF created with Face Dancer, available for free in the app store.

I'm available for one-on-one help at CodeMentor
Sign In or Register to comment.