Adjusting Layout for Keyboard

Auto Layout when the Keyboard is Shown and Hidden

When I first started using Auto Layout, I found it particularly frustrating that previously simple layouts seemed so much harder to implement. So now I find it especially rewarding when Auto Layout makes previously difficult or tedious layouts trivial. One such example is responding to the appearance of the system keyboard. In this article I’ll show you a simple approach for adjusting layout in response to the system keyboard as Apple recommends:

When asked to display the keyboard, the system slides it in from the bottom of the screen and positions it over your app’s content. Because it is placed on top of your content, it is possible for the keyboard to be placed on top of the text object that the user wanted to edit. When this happens, you must adjust your content so that the target object remains visible.

Auto Layout and the Keyboard

To demonstrate this I’ve made a simple app that shows a picture and a text field. The text field contains the editable name of the picture.

Auto Layout Keyboard HiddenWhen the keyboard is displayed the view’s layout should look like this:

Auto Layout Keyboard ShownPhoto by Gemma Stiles

Auto Layout – Constraints

There’s several important aspects for this layout:

  • The image should always maintain its original aspect
  • There is always a minimum 8 point vertical space between the image and the text field
  • There is always a minimum 10 point vertical space between the text field and the keyboard

Additionally we want to animate the view’s layout changes when the keyboard is shown and hidden. So to get started we need to add a  UIImageView:

Auto Layout Keyboard IB

Then set its contentMode  to ScaleAspectFit

Auto Layout Keyboard Image View

 

Pin the left, top, and right to its superview’s margins:

Auto Layout Keyboard Image View Constraints

To finish the views we add a UITextField  and a hidden UIView. The hidden UIView is a space view. It allows us to specify a constant padding between either the bottom of the view or the top of the keyboard:

Auto Layout Keyboard Text Field

For the UITextField  we pin its top, 8 points to the bottom of the  UIImageView. Its left and right are pinned to its superview’s margins. It’s bottom is pinned to the spacer view’s top. Finally we give it a constant height of 50 points:

Auto Layout Keyboard Text Field Constraints

 

The final constraints required are for the spacer view. We don’t care about the width so we just pin its left and right to its superview’s margins. Its bottom is pinned to the bottom layout guide. Its height is 10 points which ensures the padding we require.

Auto Layout Keyboard Spacer View Constraints

 

At this point we’ve set up our views and all their required constraints. Looking at Preview in the Assistant Editor our layout looks good on both a 3.5 inch display and a 5.5 inch display:

Auto Layout Keyboard Preview

Auto Layout – Animating

We’ve achieved a layout that is correctly adjusting to devices with different widths and heights. This is great, because all we need to do now is adjust the space between the spacer view and the bottom layout guide. This is done by adjusting the constant property on the corresponding NSLayoutConstraint.

Adjusting the constant value will be done in code in response to the keyboard being shown or hidden. First the constraint needs to be connected from the storyboard to our UIViewController  subclass. This is done by control clicking and then dragging from the constraint between the spacer view and the bottom layout guide:

Auto Layout Keyboard Wiring Up

 

Now we’re ready to write the code required to make everything functional. In order to respond to the keyboard being shown and hidden, we need to listen for the appropriate UINotification objects. We add observers in viewWillAppear and remove them in viewWillDisappear. This ensures we start observing once the view appears and stop once it disappears:

Next, we implement the functions that the notifications will call. These are simple functions that simply re-route to a function that will handle the layout update for us:

The NSNotification object contains useful data about how the keyboard will be displayed. We’re interested in the keyboard’s animation duration and its animation curve. This data tells us how we should animate our layout changes. The notification also contains the frame that this keyboard will have at the end of the animation. Pulling these constants out is mostly straightforward.

However, the  animationCurve requires us to jump through some hoops. In order to animate the change to the constraint’s constant, we’ll be using the class method  animateWithDuration:delay:options:animations:completion: on UIView. In order to specify the animation curve we pass in a  UIViewAnimationOptions value to this method. This is a mask of options as defined in  UIViewAnimationOptions. In particular the values for animation curves must be shifted to the left by 16 bits. Once we have the shifted value for the animation curve, we then use the fromRaw function onUIViewAnimationOptions struct to get the value we need.

To accommodate the space where the keyboard will be, we need to adjust our bottomLayoutConstraint’s constant. Effectively we want the constant to be 0 when the keyboard is not visible. When the keyboard is visible we want the constant to reflect the space from the bottom of our view to the top of the keyboard. To calculate the constant we just need  CGRectGetMaxY(view.bounds) - CGRectGetMinY(convertedKeyboardEndFrame). After updating the constraint’s constant, the final step is to animate the layout change.

That’s it! Apart from some boiler plate code to interact with the keyboard notification, adjusting our layout boils down to a single line of code that changes a constraint’s constant. The full source for this project is available here.

9 thoughts on “Auto Layout when the Keyboard is Shown and Hidden

  1. Yes!! I would never have considered that you could make a constraint into an outlet. That is really going to save me a trillion hours of work. Thanks.

  2. Hello, thanks for the tutorial. I’ve tried the tutorial (in Objective-C) and it works when the keyboard is shown but it doesn’t work when the keyboard is hidden.
    I change this line of code

    bottomSpacer.constant = CGRectGetMaxY(viewSpacer.bounds) – CGRectGetMinY(convertedKeyboardEndFrame);

    into

    bottomSpacer.constant = bottomSpacer.constant + CGRectGetMaxY(viewSpacer.bounds) – CGRectGetMinY(convertedKeyboardEndFrame);

    And it works perfectly now. Thanks once again! :)

    • Hello, Any chance you could post your Objective C code? I am having a time trying to convert from a normal layout to a auto layout and that would help a lot. Thanks!!!

  3. Anyone who makes a toot implementing auto-layout, animations and SWIFT is a stud. Thank you.

    Btw, I have put my tableView, textView and message cell in a scrollView but the message text is not auto-scrolling to the top of the keyboard. The scrollView doesn’t do this automatically? Do I need to execute some additional methods?

    Cheers

  4. So, reading through your entire tutorial: The solution is to create a spacer UIView whose bottom correlates to the top of the keyboard. Question: Why not keep the bottom and adjust the top?

  5. This is converted to Objective C.

    NSDictionary *userInfo = notification.userInfo;

    CGFloat animationDuration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
    CGRect keyboardEndFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
    CGRect convertedKeyboardEndFrame = [self.tableView convertRect:keyboardEndFrame fromView:self.window];
    NSUInteger rawAnimationCurve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] unsignedIntegerValue];
    UIViewAnimationOptions animationCurve = (UIViewAnimationOptions)rawAnimationCurve << 16;

    [UIView animateWithDuration:animationDuration delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState | animationCurve animations:^{
    self.tableView.y -= CGRectGetMaxY(self.tableView.bounds) – CGRectGetMinY(convertedKeyboardEndFrame);
    } completion:nil];

Leave a Reply to Yonathan Randyanto Cancel reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">