Skip to content

Instantly share code, notes, and snippets.

@dlo
Last active February 26, 2021 07:33
Show Gist options
  • Save dlo/8572874 to your computer and use it in GitHub Desktop.
Save dlo/8572874 to your computer and use it in GitHub Desktop.
How to adjust a view's height with Auto Layout when a keyboard appears or disappears in iOS 7.

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.

Setting Up

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;

Defining the View

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];;
}

Notification Handlers

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];
}

Wrapping Up

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!

File List

#import <UIKit/UIKit.h>
@interface HeightAdjustingViewController : UIViewController
@end
#import "HeightAdjustingViewController.h"
@interface HeightAdjustingViewController ()
@property (nonatomic, strong) UIView *adjustingView;
@property (nonatomic, strong) NSLayoutConstraint *bottomConstraint;
- (void)keyboardWillHide:(NSNotification *)sender;
- (void)keyboardDidShow:(NSNotification *)sender;
@end
@implementation HeightAdjustingViewController
- (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 addConstraint:[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];;
}
#pragma mark - Notification Handlers
- (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];
}
- (void)keyboardWillHide:(NSNotification *)sender {
self.bottomConstraint.constant = 0;
[self.view layoutIfNeeded];
}
@end
@AndreyPShevtsov
Copy link

I think there's a mistake
self.bottomConstraint.constant = newFrame.origin.y - CGRectGetHeight(self.view.frame);
and should be vice versa
self.bottomConstraint.constant = CGRectGetHeight(self.view.frame) - newFrame.origin.y;

@Igotit
Copy link

Igotit commented Oct 21, 2019

@AndreyPShevtsov +1. It should be vice versa.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment