Think in G

Never stop ThinkinG…

Practicing Auto Layout — An example of keyboard sensitive layout

with 24 comments

Since the iOS 6 has been officially released, we can now talk about some cool stuffs publicly.
I think that among those features, the Auto Layout is one of the most remarkable ones.

So I decided to write some posts on this topic. Because the official documents cover the motivations, theories, visual format language… all the parts of the layout magic, I don’t want to repeat those. I’ll try to focus on practicing Auto Layout.

Keyboard sensitive layout

The keyboard sensitive layout is very common in iOS apps, in which the view(s) will automatically shrink or extend when the keyboard comes out or disappears.

OK, ready? Let’s go!

1. Creating the view hierarchy

Start a new project with single view application. Drag a UITextView and a UIButton into the controller’s default view. Make these two views extend to fill the parent view. Keep the button’s height as the system default (44 points).

2. Setup the constraints

Now expand the “Constrants” node in Interface Builder. We can find that the Interface Builder has already generated some constraints for us. The purple icon indicates that it’s managed by IB, and the blue ones are user-managed constraints. A user-managed constraint can be deleted manually. And each time you create or delete a constraint, Interface Builder may recalculate those constraints to determine if there’s need to generate non-user-managed constraint. So you have to take care of the change of the automatic generated constraint.

In this example, we will define 3 user-managed constraints in vertical space:

  • The top spacing between the text view and the parent view.
  • The spacing between the text view and the button.
  • The bottom spacing between the button and the parent view.

Set constant value of those constraints to “0” to make the subviews fill the parent bounds.

3. Creating the outlet of the constraint to make it changeable by code

When the keyboard comes out, we need to change some spacing to make the new layout adaptive to the keyboard. But with Auto Layout, we have to remember these 2 things:

  • All the frames of views are calculated by the Auto Layout system. We should not arbitrarily change the frame of some view as what we have done before.
  • The constraints of the view of the root controller are managed by the system, we should not take the charge of it.

According to the concerns above, among the user-managed constraints we have created, the bottom spacing constraint is best suitable for this task. So, we create an referencing outlet named “keyboardHeight” to the constraint.

4. Coding for keyboard notification

OK, now let’s code for the keyboard events.

#import "PALViewController.h"

@interface PALViewController ()
@property (weak, nonatomic) IBOutlet UITextView *textView;
//The outlet of the bottom spacing constraint
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *keyboardHeight;
- (IBAction)dismissKeyboard:(id)sender;


@implementation PALViewController

- (void)didReceiveMemoryWarning
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.

- (void)observeKeyboard {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillChangeFrameNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];

- (void)viewDidLoad
    [super viewDidLoad];
    // Begin observing the keyboard notifications when the view is loaded.
    [self observeKeyboard];

// The callback for frame-changing of keyboard
- (void)keyboardWillShow:(NSNotification *)notification {
    NSDictionary *info = [notification userInfo];
    NSValue *kbFrame = [info objectForKey:UIKeyboardFrameEndUserInfoKey];
    NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
    CGRect keyboardFrame = [kbFrame CGRectValue];

    CGFloat height = keyboardFrame.size.height;

    NSLog(@"Updating constraints.");
    // Because the "space" is actually the difference between the bottom lines of the 2 views,
    // we need to set a negative constant value here.
    self.keyboardHeight.constant = -height;
    [UIView animateWithDuration:animationDuration animations:^{
        [self.view layoutIfNeeded];

- (void)keyboardWillHide:(NSNotification *)notification {
    NSDictionary *info = [notification userInfo];
    NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];

    self.keyboardHeight.constant = 0;
    [UIView animateWithDuration:animationDuration animations:^{
        [self.view layoutIfNeeded];

- (IBAction)dismissKeyboard:(id)sender {
    [self.textView resignFirstResponder];

First, we should register the observer as before. But, in the callback methods, there are slight changes to the prior code. We used to recalculate and set the frames of some view according to the keyboard’s frame. By adopting Auto Layout, we now turn to code against the constraints instead.

Line 38-46 shows how to obtain the height of keyboard and update the layout when keyboard appears or changes.

Now run the sample app, let’s see if it works.
When you tap the text view, the keyboard appears, and the text view will automatically shrink. When the keyboard dismissed, the button will slide down, and the text view will fill the rest part of the screen again.

5. How about changing the orientation?

Apple has said that a friendly UI will not assume the users to hold their phones in a particular way. When the phone is rotated, the keyboard’s frame will change as well, hence, our constraints should be changed subsequently. Unfortunately,  the keyboard’s frame we get in the callback is always represented in the portrait orientation. We need to handle this manually.

All we have to do is adding a piece of code in the -keyboardWillShow: callback.

// The callback for frame-changing of keyboard
- (void)keyboardWillShow:(NSNotification *)notification {
    NSDictionary *info = [notification userInfo];
    NSValue *kbFrame = [info objectForKey:UIKeyboardFrameEndUserInfoKey];
    NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
    CGRect keyboardFrame = [kbFrame CGRectValue];

    BOOL isPortrait = UIDeviceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation);
    CGFloat height = isPortrait ? keyboardFrame.size.height : keyboardFrame.size.width;
    NSLog(@"The keyboard height is: %f", height);

    NSLog(@"Updating constraints.");
    // Because the "space" is actually the difference between the bottom lines of the 2 views,
    // we need to set a negative constant value here.
    self.keyboardHeight.constant = -height;

    [UIView animateWithDuration:animationDuration animations:^{
        [self.view layoutIfNeeded];

Again, let’s try it.

Perfect! The Auto Layout just works as magic!


In this example, we have learned how to implement a common case in iOS application. With Auto Layout, we are now talking to the constraints instead of directly manipulating view frames.

What’s the next

Although Auto Layout looks easy, sometimes you see strange logs, and then get confused about constraint problems. What’s over-constraint and under-constraint? How to prevent them and created well-defined constraint? I think those are why some developers reluctant to adopt Auto Layout.

The next posts will focus on those topics.

Get the sample codes.

Written by

September 24th, 2012 at 3:41 pm

Posted in 开发,软件

Tagged with , , ,

24 Responses to 'Practicing Auto Layout — An example of keyboard sensitive layout'

Subscribe to comments with RSS or TrackBack to 'Practicing Auto Layout — An example of keyboard sensitive layout'.

  1. Nice technique, and a lot simpler than things used to be – thanks!

    One slight tweak that’s needed is in the animation – the way the code stands, the layout change is happening in the next run loop which is after the animation has completed. This way sets the constraint and flags the need for the constraints to be updated; then sets the layout within the animation block:

    self.keyboardHeight.constant = newValue;
    [self.view setNeedsUpdateConstraints];

    [UIView animateWithDuration:animationDuration animations:^{
    [self.view layoutIfNeeded];

    This way the change in constraint happens at the same speed as the keyboard appears, so they seem to happen together.

    Tim Duckett

    19 Oct 12 at 16:51

  2. Hi Tim, thank you! I watched the WWDC video again, and I’ve updated the sample codes as you mentioned.

    Since I don’t use any customized view in this sample, I’ve omitted ” [self.view setNeedsUpdateConstraints]; “.


    20 Oct 12 at 19:37

  3. You need this for landscape support since the frame doesnt rotate.

    CGFloat height;
    if (!UIDeviceOrientationIsLandscape([[UIDevice currentDevice] orientation]))
    height = keyboardFrame.size.height;
    height = keyboardFrame.size.width;

    Maciej Swic

    26 Nov 12 at 20:57

  4. Incredibly disappointing that NSConstraints don’t respond to the changing UIView size when the keyboard appears/disappears. How unfortunate.

    Andrew Zimmer

    30 Jan 13 at 06:08

  5. Thanks a lot!!! I’ve been trying to set a fixed frame size with the auto layout and couldn’t understand what’s the problem since you don’t get any errors but the frames aren’t updated!

    Now it works perfectly! Thanks!

    Chen Avnery

    12 Jun 13 at 14:49

  6. […] looks like a reasonable solution: An example of keyboard sensitive layout (GitHub source) but one potential issue I see is what happens when a user rotates the device and […]

  7. This is a great tutorial, thank you!

    I use it together will a table view instead of a text view. If the table view has cells above the dismiss button (there is also a text field), I’d like to scroll the table view content up as well so that the last visible cell is not covered after the keyboard appears. Any ideas how to do that as elegant would be much appreciated.


    28 Jul 13 at 11:47

  8. excellent – many thanks


    5 Sep 13 at 23:47

  9. Thank you

    Evgeni Petrov

    16 Sep 13 at 20:58

  10. Hi Yi,

    Is there a better way to do this in iOS7 without listening to any notifications (maybe with UIAttachmentBehaviour)?


    Sumeru Chatterjee

    22 Nov 13 at 19:14

  11. keyboardFrame = [self.view convertRect: keyboardFrame fromView: nil];
    Instead of examining the orientation yourself.

    Mark Collette

    2 Dec 13 at 13:39

  12. UIAttachmentBehaviour won’t help you here sir

    Adam Waite

    1 Apr 14 at 06:09

  13. Whats the code in wit ?


    29 Nov 14 at 06:14

  14. What’s the code in Swift ?


    29 Nov 14 at 06:14

  15. Excellent code. A fairly simple solution to a nasty little AutoLayout problem.

    It’s just a shame that there’s no onscreen-keyboard-readjusting already built into Apple’s nasty little AutoLayout stuff.

    Mike Gledhill

    23 Dec 14 at 22:27

  16. Yes – and this is particularly important in iOS 8.1.
    (Without this line, I was sometimes being told that my iPad’s onscreen keyboard was 1024 pixels high, in landscape mode.)

    Mike Gledhill

    23 Dec 14 at 22:27

  17. Hugo

    5 Jan 15 at 21:18

  18. Be careful using this code.

    Prior to iOS 8, it worked beautifully, but now, with an optional area on the top of the onscreen keyboard, where the user can display text prediction suggestions, the height of the keyboard *can* vary. And, critically, when they turn on text prediction, it’ll call the “keyboardWillShow” handler *again*, causing this code to think that it needs to decrease the “constant” value by another 220 pixels.

    I have used this code in my app, but in the “viewWillDisplay” function, I stored the initial value of the constraint’s “constant” value. Then, in the “keyboardWillShow” handler, I set the new “constant” to be the current keyboard’s height plus the original constant value.

    Basically, you can no longer use line 15 of this function:

    self.keyboardHeight.constant = -height;

    Mike Gledhill

    6 Jan 15 at 20:36

  19. I had my bottom constraint as distance between a view and its superview bottom margin.

    I needed to have self.keyboardHeight.constant = height; since the distance is a positive value.


    8 Jul 15 at 13:37

  20. thanks, this post saved lot of my time :)

    Prashant Rai

    10 Aug 15 at 16:34

  21. Thank you so much I owe so much to this article. YOU ARE AMAZING THANK YOU

    Matthew Bordas

    18 Feb 16 at 05:01

  22. Thank you for pointing this out, I think you could just test the constant value via an if statement and not update it in the case you mentioned?

    Matthew Bordas

    18 Feb 16 at 05:08

  23. In this StackOverflow article, I describe how I implemented this functionality, using constraints.

    In particular, this shows how to cope with the “text prediction” bar being turned on and off.

    Mike Gledhill

    18 Feb 16 at 15:25

Leave a Reply