Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to resize UITextView on iOS when a keyboard appears?

There's UITextView inserted into tab in UITabBarController (on the iPhone).

  1. Fill UITextView with a lot of lines.
  2. Show a keyboard to edit text.

What's happen? The keyboard hide a half of UITextView with cursor. Can't edit the text as the result.

How to resolve the issue for all Apple mobile devices (with different screen resolution)? Thanks a lot for help!

like image 271
Dmitry Avatar asked Aug 24 '11 02:08

Dmitry


3 Answers

The best result was reached by the following code. Also don't forget to set background color to UIView and place UITextView before other top-screen controls (e.g. UITabBar).

Editing of a text in the end still isn't perfect now. You may try to improve.

FirstViewController.h:

@interface FirstViewController : UIViewController {
    IBOutlet UIBarButtonItem *buttonDone;
    IBOutlet UITextView *textView;
    UITabBarController* tabBarController; // set from superview in AppDelegate (MainWindow.xib)
}

@property (nonatomic, retain) UITabBarController* tabBarController;

FirstViewController.m:

@synthesize tabBarController;

- (void)viewDidAppear:(BOOL)animated
{
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShown:) name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}

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

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

    [[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
    [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
    [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];

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

    CGRect newFrame = textView.frame;
    CGRect keyboardFrame = [self.view convertRect:keyboardEndFrame toView:nil];
    keyboardFrame.size.height -= tabBarController.tabBar.frame.size.height;
    newFrame.size.height -= keyboardFrame.size.height * (up?1:-1);
    textView.frame = newFrame;

    [UIView commitAnimations];   
}

- (void)keyboardWillShown:(NSNotification*)aNotification
{
    buttonDone.enabled = true;
    [self moveTextViewForKeyboard:aNotification up:YES]; 
}

- (void)keyboardWillHide:(NSNotification*)aNotification
{
    buttonDone.enabled = false;
    [self moveTextViewForKeyboard:aNotification up:NO]; 
}

P.S. It's hard to code for iOS without stackoverflow...

like image 130
Dmitry Avatar answered Nov 06 '22 18:11

Dmitry


With Auto Layout, it's much easier (provided you understand Auto Layout) to handle:

Instead of trying to identify and resize the affected views, you simply create a parent frame for all your view's contents. Then, if the kbd appears, you resize the frame, and if you've set up the constraints properly, the view will re-arrange all its child views nicely. No need to fiddle with lots of hard-to-read code for this.

In fact, in a similar question I found a link to this excellent tutorial about this technique.

like image 9
Thomas Tempelmann Avatar answered Nov 06 '22 18:11

Thomas Tempelmann


I ran into several issues trying to get my text view to scroll and animate correctly for both iOS 7 and iOS 8, and with the new QuickType feature. At first I was focused on animating the scroll view insets, but the behaviour differed between iOS 7 and 8 and could not get it working correctly for both.

Then I realized I can simplify things by just focusing on the frame and this worked for me with a lot more simple code. In summary:

  • register for the UIKeyboardDidChangeFrameNotification (this will notify when the QuickType is shown/hidden as well).
  • figure out how much vertical space you need to change your text view's frame by.
  • animate the frame size change.

Here is some code that illustrates the above:

- (void)viewDidLoad {
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidChangeFrameWithNotification:) name:UIKeyboardDidChangeFrameNotification object:nil];
}

- (void)keyboardDidChangeFrameWithNotification:(NSNotification *)notification {
    CGFloat keyboardVerticalIncrease = [self keyboardVerticalIncreaseForNotification:notification];
    [self animateTextViewFrameForVerticalOffset:keyboardVerticalIncrease];
}

- (CGFloat)keyboardVerticalIncreaseForNotification:(NSNotification *)notification {
    CGFloat keyboardBeginY = [notification.userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue].origin.y;
    CGFloat keyboardEndY = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue].origin.y;
    CGFloat keyboardVerticalIncrease = keyboardBeginY - keyboardEndY;
    return keyboardVerticalIncrease;
}

- (void)animateTextViewFrameForVerticalOffset:(CGFloat)offset {
    CGFloat constant = self.bottomConstraint.constant;
    CGFloat newConstant = constant + offset;
    self.bottomConstraint.constant = newConstant;
    [self.view layoutIfNeeded];
    [UIView animateWithDuration:0.5 animations:^{
        [self.view layoutIfNeeded];
    }];
}

A quick note on the animation. I used Autolayout so I chose to animate the text view's NSAutoLayoutConstraint, not the frame directly. And to do this, I call [self.view layoutIfNeeded] before and inside the animation block. This is the correct way of animating constraints. I found this tip here.

like image 3
abc123 Avatar answered Nov 06 '22 18:11

abc123