Resizing a UITextView automatically with the keyboard

One of the biggest UI no-nos you can perform in iOS is creating a UITextView that assumes the size of the keyboard on the screen before the keyboard even shows up. You should be dynamically resizing your text views by looking at the dimensions of the keyboard, and not assuming you know the dimensions.

Why? Try enabling a Japanese keyboard and see how your app performs. You’ll notice that the keyboard is taller than the English keyboard – it’s to give room to a typeahead area for creating the glyphs. Now how does your app behave?

There is a quick and easy way you can resize automatically. In my example, I have a new view with a nearly full screen-sized text view showing. When the user taps into the text view, the text view is then resized to leave a bit of gap around it and the keyboard displays underneath.

The text view without a keyboard

blanktextview

standardkeyboard
japanesekeyboard

After the user taps in the text view, the keyboard appears. An example of an English keyboard is at left and a Japanese keyboard at the right. Notice any difference? What should happen is your app just figures out how much space you have left after the keyboard is displayed and resize accordingly. If you switch between keyboards, you have to account for visual changes as well. Here is the code I wrote to handle this properly:

- (void)viewWillAppear:(BOOL)animated {
[textView setText:_notesText];

// Register notifications for when the keyboard appears
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}

- (void)viewWillDisappear:(BOOL)animated {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)keyboardWillShow:(NSNotification*)notification {
[self moveTextViewForKeyboard:notification up:YES];
}

- (void)keyboardWillHide:(NSNotification*)notification {
[self moveTextViewForKeyboard:notification up:NO];
}

- (void)moveTextViewForKeyboard:(NSNotification*)notification up:(BOOL)up {
NSDictionary *userInfo = [notification userInfo];
NSTimeInterval animationDuration;
UIViewAnimationCurve animationCurve;
CGRect keyboardRect;

[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
animationDuration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
keyboardRect = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
keyboardRect = [self.view convertRect:keyboardRect fromView:nil];

[UIView beginAnimations:@"ResizeForKeyboard" context:nil];
[UIView setAnimationDuration:animationDuration];
[UIView setAnimationCurve:animationCurve];

if (up == YES) {
CGFloat keyboardTop = keyboardRect.origin.y;
CGRect newTextViewFrame = textView.frame;
originalTextViewFrame = textView.frame;
newTextViewFrame.size.height = keyboardTop - textView.frame.origin.y - 10;

textView.frame = newTextViewFrame;
} else {
// Keyboard is going away (down) - restore original frame
textView.frame = originalTextViewFrame;
}

[UIView commitAnimations];
}

The view controller adds an observer for when the keyboard shows up and disappears. When the keyboard is coming up, we record the original frame of the text view – this makes it easier to restore the text view when the keyboard goes away. In this specific instance, the keyboard cannot be dismissed since enter is set to put carriage returns in the text box.

7 thoughts on “Resizing a UITextView automatically with the keyboard

    1. astralbodies

      No, I don’t think it does. I just used this on an iPad app and it messed up big time. I’ll have to fix it and post a replacement code block.

      Like

  1. Markus

    After tweaking the code a bit, I got this to work flawless even for japanese keyboards. It also handles the keyboard changes while typing. But I had to change the up animation to this:
    CGFloat keyboardTop = keyboardRect.origin.y;
    CGRect newTextViewFrame = self.view.frame;
    newTextViewFrame.size.height = self.originalTextViewFrame.size.height – keyboardRect.size.height – 10.0f;
    self.view.frame = newTextViewFrame;
    I set the self.originalTextViewFrame in viewWillAppear:
    The original code failed (at least for me) when there was more than a “viewfull” of text, then scrolling didn’t work properly and I couldn’t see the last lines.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s