This gist outlines how to resize a view when a keyboard appears using Auto Layout (there are a bunch of code samples out there that manually adjust the view's frame, but that's just so 2013). The method I outline below works universally on both iPhone and iPad, portrait and landscape, and is pretty darn simple.
The first thing to do is to define our containing view controller, the view, and the bottom constraint that we'll use to adjust its size.
Here's HeightAdjustingViewController.h. We don't need to expose any public properties, so it's pretty bare.
@interface HeightAdjustingViewController : UIViewController
@end
Now, in the class extension in our implementation file, we define a view
and a bottomConstraint
, which will adjust the bottom position of our view when the keyboard appears and disappears.
@property (nonatomic, strong) UIView *adjustingView;
@property (nonatomic, strong) NSLayoutConstraint *bottomConstraint;
We also define two methods to send the keyboard hide and show notifications to.
- (void)keyboardWillHide:(NSNotification *)sender;
- (void)keyboardDidShow:(NSNotification *)sender;
We can now move on to the implementation. In our viewDidLoad
method, we'll set up the view, add observers for the hide and show notifications, and define our bottomConstraint
.
- (void)viewDidLoad {
[super viewDidLoad]
self.adjustingView = [[UIView alloc] init];
self.adjustingView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.adjustingView];
NSDictionary *views = @{@"view": self.adjustingView,
@"top": self.topLayoutGuide };
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"[top][view]" options:0 metrics:nil views:views]];
self.bottomConstraint = [NSLayoutConstraint constraintWithItem:self.adjustingView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.bottomLayoutGuide attribute:NSLayoutAttributeTop multiplier:1 constant:0];
[self.view addConstraint:self.bottomConstraint];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];;
}
Great, we've got everything set up. Our visual layout constraints pin the adjusting view to the topLayoutGuide
, and the bottomConstraint
pins the bottom of the view to the bottomLayoutGuide
(which we'll soon adjust using the constant).
Now it's time to set up our keyboard notification handlers.
The UIKeyboardDidShowNotification
sends an NSNotification
with a userInfo
dictionary containing a key that has the final CGRect
of the keyboards position on the screen. We're going to take that rectangle,
CGRect frame = [sender.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
convert it into the coordinate system of the current view (this is necessary in the situation where the view's view controller is being presented modally, such as in a form sheet on the iPad, and is offset from the main windows coordinate system, which the keyboard's frame is given to us in),
CGRect newFrame = [self.view convertRect:frame fromView:[[UIApplication sharedApplication] delegate].window];
and then adjust the bottom constraint by the starting position of the keyboard in the Y-axis as offset from the height of the current superview.
self.bottomConstraint.constant = newFrame.origin.y - CGRectGetHeight(self.view.frame);
I know, it's a mouthful. After updating the constraint's constant, we call layoutIfNeeded
to re-layout our subviews.
Here's the completed keyboardDidShow:
:
- (void)keyboardDidShow:(NSNotification *)sender {
CGRect frame = [sender.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect newFrame = [self.view convertRect:frame fromView:[[UIApplication sharedApplication] delegate].window];
self.bottomConstraint.constant = newFrame.origin.y - CGRectGetHeight(self.view.frame);
[self.view layoutIfNeeded];
}
Now, keyboardWillHide:
is a bit simpler. We don't care what the keyboard frame is; we just want to put things back to where they were before (pin the adjustingView
back to the bottom). We do this by just reassigning the bottomConstraint
's constant back to 0.
- (void)keyboardWillHide:(NSNotification *)sender {
self.bottomConstraint.constant = 0;
[self.view layoutIfNeeded];
}
And that's it. You can browse the files in their entirety below. Of course, I didn't add a text field that would make the keyboard appear, but I'll leave that as an exercise for the reader. Enjoy!
Hi Dan, I like the simple approach you have, but I am having trouble getting it to work. Essentially nothing happens when keyboard appears. I am not exactly clear on how to set up the view hierarchy. I have a (Storyboard) view controller, with two UITextFields. I would like the TFs to scroll up when keyboard appears over them. But should the TFs be in a subview of the main ViewController view? Given you add the adjustingView as a subview, perhaps this is what's need to make the constraint.constant changes have any affect?
Also, a couple of things I notice:
In viewDIdLoad: (line 25):
[self.view addConstraint:...] looks like it should be [self.view addConstraints:...]
in keyboardDidShow: (line 40):
The bottomConstraint.constant is always negative. This is not what I expected...
Any info/tips much appreciated!
Xcode 5.1, ios 7.1.
cheers,
dan