Offset UITableView Content When Keyboard Appears

Posted on May 18, 2013 by Brandon

Scroll the tableview or textview content when the keyboard pops up.

This post will show how to adjust the content insets of a uitableview or uitextview when the keyboard pops up, so nothing is hidden underneath. I'm just going to jump right into the code for this one because this post is more for reference whenever this topic comes up. I'm not going to do a full tutorial for something like this.

The way we will accomplish this is by first registering for keyboard notifications, then adjusting the content inset based on the height of the keyboard.

Register for notifications

In your viewDidLoad or viewWillAppear method add the code to register for keyboard notifications.

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

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillHide:)
                                                 name:UIKeyboardWillHideNotification
                                               object:nil];
}

We add this view controller as the observer. We pass in the selector of the method that should be called when this notification occurs, which we will write in a minute. Having the colon at the end of the selector is important because this tells it that it should pass the notification parameter to the method. Then we give the name of the notification we want to observe, UIKeyboardWillShowNotification and UIKeyboardWillHideNotification, which are predefined notifications. The object parameter is asking us to define which object will post this notification, which according to the documentation should be nil for the keyboard notifications.

Wherever you register for these notifications, you should unregister as an observer in the opposing method (viewDidAppear vs viewDidDisappear). So, if you put them in viewDidLoad, you can unregister in dealloc since viewDidUnload is deprecated.

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

This line simply removes this view controller as an observer of all notifications. Be sure not to call [super dealloc] now with ARC.

Apply the content insets

Now we need to write the keyboardWillShow: method we registered as the selector to call.

- (void)keyboardWillShow:(NSNotification *)notification
{    
    CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    UIEdgeInsets contentInsets;
    if (UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation])) {
        contentInsets = UIEdgeInsetsMake(0.0, 0.0, (keyboardSize.height), 0.0);
    } else {
        contentInsets = UIEdgeInsetsMake(0.0, 0.0, (keyboardSize.width), 0.0);
    }

    self.tableView.contentInset = contentInsets;
    self.tableView.scrollIndicatorInsets = contentInsets;
    [self.tableView scrollToRowAtIndexPath:self.editingIndexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}

To get the keyboard size, we first get the userInfo dictionary from the notification object, which stores any additional objects that our receiver might use. From that dictionary we can get the CGRect object describing the keyboard's frame by using the key UIKeyboardFrameBeginUserInfoKey. Then we get the CGSize value from that CGRect.

The coordinates given to us do not take into account rotation, so we need to check if the app is in portrait or landscape mode. If we are in portrait we create a UIEdgeInsets object with the height of the keyboard. If in landscape we use the width since this is actually the height.

Next we apply the content inset to the tableview, or this could be a textview, scrollview, etc. This way is better than trying to change the frame of the view around to adjust. The tableview will stay in the same position, but it will move all of its content up above the keyboard.

Rotation is also handled this way because the UIKeyboardWillShowNotification is posted on an orientation change if the keyboard is open.

Restore original position

Now we just have to reset the inset when the keyboard hides.

- (void)keyboardWillHide:(NSNotification *)notification
{
    self.tableView.contentInset = UIEdgeInsetsZero;
    self.tableView.scrollIndicatorInsets = UIEdgeInsetsZero;
}

As pointed out by Alaeddine in the comments, you might want to wrap the setting of the insets in an animation. The usual keyboard animation rate seems to be 0.3 seconds, but it could be different based on region, such as when international users switch between keyboard sizes. The best way to get the duration of the animation is to grab it from the userInfo dictionary that comes in the keyboard notification. Be sure to make the change in both keyboardWillShow: and keyboardWillHide:. The change that needs to be made is as follows...

NSNumber *rate = notification.userInfo[UIKeyboardAnimationDurationUserInfoKey];
[UIView animateWithDuration:rate.floatValue animations:^{
        self.tableView.contentInset = // insert content inset value here
        self.tableView.scrollIndicatorInsets = // insert content inset value here
}];

Conclusion

That's all there is to it. Grab the code on my Gist.


comments powered by Disqus