AppsDev
AppsDev

Reputation: 12499

Scrolling a table view with text fields when "Next" return button in keyboard

I have a grouped table view with custom cells, and these cells have a UITextField. I've set the returnKeyType of the text fields to be UIReturnKeyNext (except for the last row), and I've implemented their textFieldShouldReturn: method this way:

- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
   id firstResponderCell = textField.superview.superview ;
   NSInteger firstResponderRow = [self.tableView indexPathForCell:firstResponderCell].row ;
   NSIndexPath* nextCellIndexPath = [NSIndexPath indexPathForRow:firstResponderRow+1 inSection:0] ;
   CustomCell* nextCell = (CustomCell*)[self.tableView cellForRowAtIndexPath:nextCellIndexPath] ;

   if (nextCell) {
      [nextCell.cellTextField becomeFirstResponder] ;
   }
   else
      [textField resignFirstResponder] ;
   return YES ;

}

But I'm not able to scroll the table view content in order to make the current active text field visible above the keyboard. I successfully managed this when I did not implement the "Next" button functionality in keyboard and keyboard was just hidden when "Enter" button was tapped, and then the keyboard was again shown when another text field was tapped, since I'm listening for UIKeyboardDidShowNotification and UIKeyboardDidHideNotification. But now that I want a "Next" button in keyboard and keyboard is not being hidden when tapping the button, I don´t know how to scroll to the active text field properly.

Thanks in advance

Upvotes: 2

Views: 1259

Answers (2)

brainforked
brainforked

Reputation: 139

This is the most easiest and efficient way to achieve this:

Add the following constants:

static const CGFloat KEYBOARD_ANIMATION_DURATION = 0.3;
static const CGFloat MINIMUM_SCROLL_FRACTION = 0.2;
static const CGFloat MAXIMUM_SCROLL_FRACTION = 0.8;
static const CGFloat PORTRAIT_KEYBOARD_HEIGHT = 216;
static const CGFloat LANDSCAPE_KEYBOARD_HEIGHT = 162;

Add this to your view controller:

CGFloat animatedDistance;

And add these methods to your code:

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
CGRect textFieldRect =
[self.view.window convertRect:textField.bounds fromView:textField];
CGRect viewRect =
[self.view.window convertRect:self.view.bounds fromView:self.view];
CGFloat midline = textFieldRect.origin.y + 0.5 * textFieldRect.size.height;
CGFloat numerator =
midline - viewRect.origin.y
- MINIMUM_SCROLL_FRACTION * viewRect.size.height;
CGFloat denominator =
(MAXIMUM_SCROLL_FRACTION - MINIMUM_SCROLL_FRACTION)
* viewRect.size.height;
CGFloat heightFraction = numerator / denominator;
if (heightFraction < 0.0)
{
    heightFraction = 0.0;
}
else if (heightFraction > 1.0)
{
    heightFraction = 1.0;
}
UIInterfaceOrientation orientation =
[[UIApplication sharedApplication] statusBarOrientation];
if (orientation == UIInterfaceOrientationPortrait ||
    orientation == UIInterfaceOrientationPortraitUpsideDown)
{
    animatedDistance = floor(PORTRAIT_KEYBOARD_HEIGHT * heightFraction);
}
else
{
    animatedDistance = floor(LANDSCAPE_KEYBOARD_HEIGHT * heightFraction);
}
CGRect viewFrame = self.view.frame;
viewFrame.origin.y -= animatedDistance;

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];

[self.view setFrame:viewFrame];

[UIView commitAnimations];

}

- (void)textFieldDidEndEditing:(UITextField *)textField
    {
CGRect viewFrame = self.view.frame;
viewFrame.origin.y += animatedDistance;

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];

[self.view setFrame:viewFrame];

[UIView commitAnimations];

}

P.S: This one is from here.

Upvotes: 2

Wain
Wain

Reputation: 119021

This isn't a safe way of getting the cell:

id firstResponderCell = textField.superview.superview ;

I.e. It might work now but it might not work in the (near) future.

Once you know the index path you should check that it actually exists. Then you can use scrollToRowAtIndexPath:atScrollPosition:animated: to scroll the row (and make it the top row). Alternatively you can get the frame of the row and then set the contentOffset of the table view.

Upvotes: 0

Related Questions