无需关闭键盘即可平滑resize并移动UITableViewCell

我在UITableViewCell中有一个UITextView。 当用户编辑UITextView文本时,我调整了UITextView的大小,我需要调整UITableViewCell的大小,同时确保UITableViewCell始终位于键盘上方。

要在不关闭键盘的情况下调整UITableViewCell的大小,我正在执行以下操作,它可以正常工作:

[tableView beginUpdates]; [tableView endUpdates]; 

要移动tableView并让单元格在键盘上方可见,我正在执行以下操作并且它可以正常工作:

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

我的问题是我不能同时调整UITableViewCell并将它们平滑移动。 这样的东西不起作用:

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

如果我这样做,单元格会正确resize,但tableView会一直向下滚动。 我理解这种调整UITableViewCell大小的方式是异步工作的,这就是调用setContentOffset不起作用的原因,可能是因为在iOS的表视图单元大小调整处理过程中处理了setContentOffset。

我尝试了以下,并且tableview最终滚动到所需的位置。 但是,用户可以看到tableView上下移动,看起来很奇怪:

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

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

我非常感谢这个社区对我的问题的帮助,因为我已经在几天内一直在努力解决这个问题。 这些测试是在iOS 10和iOS 11的设备上进行的,结果是相同的。

提前致谢。

从上到下在单元格中正确挂钩约束也不实现heightForRowAtIndexpath

然后

在viewDidLoad中

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

在数据源中

  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 } 

我设法找到了怎么做! 有3个非常重要的细节:

  1. 在不关闭键盘的情况下调整单元格大小的方式。 (正常的重新加载会隐藏键盘)。
  2. 仅在内容偏移动画完成后才设置单元格高度。 (否则可以忽略setContentOffset)。
  3. 为tableView设置底部插图。 (否则调整单元格大小可能会向下滚动表格,隐藏我们想要在键盘上方可见的单元格。)

此解决方案已在iOS10和iOS11中使用真正的iPhone进行了测试。

这是如何(所有代码在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;} 

该技巧的第二部分仅在滚动动画结束后调整单元格大小:

 - (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]; }];} 

非常重要:键盘关闭后,顺利重新调整tableview滚动(如有必要):

 - (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; } 

一切如何开始:

 - (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;} 

用户还可以直接从一个单元格中的一个textField或textView跳转到另一个单元格中的另一个单元格,而无需关闭键盘。 这必须考虑和处理。

每当textView文本发生更改时,也应调用滚动方法,因为如果它的大小和单元格的大小也需要更改:

 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. 

也很有用:

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

在viewController中创建的有用属性:

 @property (nonatomic) BOOL textControlCellHadToMoveUpToBeVisibleOverKeyboard; 

我希望这会有用。