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:
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.
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:
Code:
//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;
else
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
animations:^{
[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
animations:
^{
//Reverse 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.
Last edited: