Sh*t my brain says and forgets about

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.

Previous

Fixing Layer Transparency Issues in Xcode

Next

Maven & Eclipse target folder + validation stabbyness

7 Comments

  1. txLUZA

    Hm. Does that code work if the view is rotated while the keyboard is expanded?

    • 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.

  2. Keith

    originalTextViewFrame is undefined

  3. Flym4n

    Works perfectly using a property for the original frame

    @property (nonatomic) CGRect originalTextViewFrame;Thanks!

  4. 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.

  5. Asawari

    Not working for me

    • Aaron Douglas

      I’m not surprised as this is from 2012, most likely iOS 5. I’ll give it a whirl and see if I can’t update the answer. 😉

Leave a Reply

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

Powered by WordPress & Theme by Anders Norén