Scroll position, UITableViews, and You

Came across a scenario on an iOS app today that didn’t have a clear answer in a single place. I basically had to cobble together a solution for my problem, and wanted to document it here.

The Problem

I have a UITableView that I’ve developed in order to mimic a form. My “form” has a single UITextField in it, but this should work with any number of text fields in a UITableView. The text field is at the very bottom of my form, and I need to set the scroll position of the table when the keyboard is shown so the field remains in the user’s view.

The Solution: Psuedo Code

  • Make the controller conform to the UITextFieldDelegate protocol
  • Implement the textDidBeginEditing method of the UITextFieldDelegate protocol to add the UITapGestureRecognizer
  • Create methods to respond to the keyboard display/hide notifications that will resize the scroll content and set the scroll position
  • Add notification observers to listen for keyboard display/hide notifications
  • Create a UITapGestureRecognizer that will be used to dismiss the keyboard when the user taps on the UITableView

The Solution: Actual Code

UITextFieldDelegate Protocol

Set your class to conform to the UITextFieldDelegate protoocal, and implement the textFieldDidBeginEditing:(UITextField *)textField method. This is where you’re going to add your gesture recognizer whenever the user taps in the field so the view knows to change scroll position.

Keyboard Notifications

Create methods to respond to the display and hiding of the keyboard. The following code was somewhat ripped from Apple’s documentation on managing scroll position for keyboards. The trick I found was to use the UITableView’s scrollToRowAtIndexPath:atScrollPosition:animated: method to get the scroll position I really wanted.

-(void)keyboardWasShown:(NSNotification *)theNotification{
    //adjust the scroll position so the zip code field is in view when the keyboard shows up
    
    NSDictionary *info = [theNotification userInfo];
    CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    //set the insets to account for the keyboard height
    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0);
    self.myTableView.contentInset = contentInsets;
    self.myTableView.scrollIndicatorInsets = contentInsets;
    
    UITableViewCell *cell = (UITableViewCell*) [[_activeField superview] superview];
    [self.myTableView scrollToRowAtIndexPath:[self.myTableView indexPathForCell:cell] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}

-(void)keyboardWillBeHidden:(NSNotification *)theNotification{
    //reset the content insets
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    self.myTableView.contentInset = contentInsets;
    self.myTableView.scrollIndicatorInsets = contentInsets;
}

Add observers to the notification center that listen for the keyboard display and hidden events so you can adjust the scroll position. I did this in the init method of my class, but there are lots of alternatives to your placement of this code.

        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil];
        
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillBeHidden:) name:UIKeyboardDidHideNotification object:nil];

Create the UIGestureRecognizer, and set it on the UITableView of your class. I created an instance variable (_tap) so I could add/remove it at various places in my code.

_tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tableTouched:)];
[self.myTableView addGestureRecognizer:_tap];

And here’s the tableTouched: method. As I said before, I have only 1 UITextField in my table, and I created an ivar (instance variable) so I could reference it quickly within the code. I removed the UIGestureRecognizer at this point because I don’t want the gesture to interfere with any otherwise normal table gestures.

-(void)tableTouched:(id)sender{
    [_activeField resignFirstResponder];
    [self.myTableView removeGestureRecognizer:_tap];
}
Scroll position, UITableViews, and You

Leave a Reply