Resizing and moving the UITableViewCell smoothly without releasing the keyboard

I have a UITextView inside a UITableViewCell. When the user edits the text of the UITextView, I resize the UITextView, and I need to resize the UITableViewCell, and also make sure that the UITableViewCell is always above the keyboard.

To resize the UITableViewCell without releasing the keyboard, I do the following and everything works fine:

[tableView beginUpdates]; [tableView endUpdates]; 

To move the View table up and show the cell above the keyboard, I do the following and it works fine:

 [tableView setContentOffset:CGPointMake(0, myDesiredScrollOffset) animated:YES]; 

My problem is that I cannot both resize the UITableViewCell and move smoothly together. Something like this will not work:

 [tableView beginUpdates]; [tableView endUpdates]; [tableView setContentOffset:CGPointMake(0, myDesiredScrollOffset) animated:YES]; 

If I do this, the cell will be resized correctly, but instead, the tableView will scroll down. I understand that this way to resize a UITableViewCell works asynchronously, and therefore the call to setContentOffset will not work, perhaps because setContentOffset is processed in the middle of the process of resizing the iOS table cell.

I, than I tried the following, and the table looks at the scroll to the right place. However, the user can see that the tableView goes up and down and looks weird:

 [tableView beginUpdates]; [tableView endUpdates]; [self performSelector:@selector(myMethodToScroll) withObject:nil afterDelay:0.01]; 

...

  - (void) myMethodToScroll { [self.tableView setContentOffset:CGPointMake(0, self.myDesiredScrollOffset) animated:YES];} 

I would really appreciate the help of this community in my problem, because I have been struggling with this problem for several days now. These tests are conducted on devices with iOS 10 and iOS 11, and the results are the same.

Thanks in advance.

0
source share
2 answers

I managed to figure out how to do it! There are 3 very important details:

  • Resize a cell without closing the keyboard. (A regular reboot will hide the keyboard).
  • Cell height adjustment is performed only after the content shift animation is completed. (Otherwise, the setContentOffset parameter may be ignored).
  • Set the bottom insert for tableView. (Otherwise, the cell size setting may scroll down the table, hiding the cell that we want to see above the keyboard.)

This solution was tested on iOS10 and iOS11 with real iPhones.

Here's how (all the code is implemented in viewController):

 - (TableViewScrollDirection) scrollToKeepEditingCellVisibleAboveVerticalPoint:(CGFloat)verticalPoint cellIndexPath:(NSIndexPath*)cellIndexPath anchorsToVerticalPoint:(BOOL)anchorsToVerticalPoint cellHeight:(CGFloat)cellHeight adjustsCellSize:(BOOL)adjustsCellSize { // Remark: verticalPoint is the desired offset above the tableView bottom. In my case the height of the keyboard covering the bottom of the tableView CGRect cellFrame = CGRectOffset([self.tableView rectForRowAtIndexPath:cellIndexPath], -self.tableView.contentOffset.x, -self.tableView.contentOffset.y - self.tableView.contentInset.top); CGFloat cellBottom = adjustsCellSize ? cellFrame.origin.y + cellHeight : cellFrame.origin.y + cellFrame.size.height; CGFloat offsetNeeded = cellBottom - verticalPoint; // Relative offset CGFloat brandNewOffset = self.tableView.contentOffset.y + offsetNeeded; // Absolute offset if ((offsetNeeded > 0) || ((offsetNeeded < 0) && anchorsToVerticalPoint)) { CGFloat elasticity = self.tableView.frame.size.height - verticalPoint; if (self.tableView.contentInset.bottom != elasticity) self.tableView.contentInset = UIEdgeInsetsMake(0, 0, elasticity, 0); // This will make sure the tableview does not scroll down when its content offset elasticity is not enough if (adjustsCellSize) [self setContentOffsetAndAdjustCellSizes:brandNewOffset]; else [self.tableView setContentOffset:CGPointMake(0, brandNewOffset) animated:YES]; if (offsetNeeded > 0) return TableViewScrollUp; else if (offsetNeeded < 0) return TableViewScrollDown; } return TableViewScrollNone;} 

The second part of the trick is to adjust the cell size only after the end of the scroll animation:

 - (void) setContentOffsetAndAdjustCellSizes:(CGFloat)contentOffset{ [UIView animateWithDuration:0.5 animations:^ { [self.tableView setContentOffset:CGPointMake(0, contentOffset) animated:NO]; } completion:^(BOOL finished) { [self.tableView beginUpdates]; // Cell height must be adjusted this way, otherwise the keyboard closes. [self.tableView endUpdates]; }];} 

Very important: after closing the keyboard, smoothly drag the table scroll (if necessary):

 - (void) keyboardDidHide:(NSNotification *)notification { if (self.tableView.contentInset.bottom != 0) [UIView animateWithDuration:0.5 animations:^ {self.tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);}]; self.activeKeyboardSize = CGSizeZero; } 

How it all starts:

 - (void) keyboardDidShow:(NSNotification*)notification { NSDictionary* info = [notification userInfo]; self.activeKeyboardSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size; CGFloat tableViewBottom = self.tableView.frame.origin.y + self.tableView.frame.size.height; CGFloat keyboardTop = self.view.frame.size.height - self.activeKeyboardSize.height; CGFloat coveringVerticalSpace = tableViewBottom - keyboardTop; if (coveringVerticalSpace <= 0) return; TableViewScrollDirection scrollDirection = [self scrollToKeepEditingCellVisibleAboveVerticalPoint:self.tableView.frame.size.height - coveringVerticalSpace - UI_MARGIN_DEFAULT anchorsToVerticalPoint:NO]; if (scrollDirection == TableViewScrollUp) self.textControlCellHadToMoveUpToBeVisibleOverKeyboard = YES;} 

The user can also scan his editing directly from one text field or textView in one cell to another in another cell without closing the keyboard. This must be taken into account and processed.

The scroll method should also be called whenever the textView text changes, because in case of its size and, therefore, the cell size also needs to be changed:

 CGFloat tableViewBottom = self.tableView.frame.origin.y + self.tableView.frame.size.height; CGFloat keyboardTop = self.view.frame.size.height - self.activeKeyboardSize.height; CGFloat coveringVerticalSpace = tableViewBottom - keyboardTop; if (coveringVerticalSpace <= 0) return; [self scrollToKeepEditingCellVisibleAboveVerticalPoint:self.tableView.frame.size.height - coveringVerticalSpace - UI_MARGIN_DEFAULT anchorsToVerticalPoint:self.textControlCellHadToMoveUpToBeVisibleOverKeyboard cellHeight:staticCellView.frame.size.height adjustsCellSize:adjustsCellSize]; // UI_MARGIN_DEFAULT is 8.0 and it gives a little margin of 8 points over the keyboard. 

Also useful:

 typedef NS_ENUM(NSUInteger, TableViewScrollDirection){ TableViewScrollNone, TableViewScrollDown, TableViewScrollUp }; 

Useful property to create in viewController:

 @property (nonatomic) BOOL textControlCellHadToMoveUpToBeVisibleOverKeyboard; 

I hope this will be helpful.

0
source

correctly binds you to the restrictions in the cell from top to bottom and does not implement heightForRowAtIndexpath

then

in viewDidLoad

 tableView.estimatedRowHeight = 200; tableView.rowHeight = UITableViewAutomaticDimension; 

in the data source

  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier:CellIdentifier1) as! logTableViewCell // your code here cell.layoutSubviews() cell.layoutIfNeeded() return cell } 
0
source

All Articles