在不关闭键盘的情况下平滑地调整大小和移动 UITableViewCell
Resize and move UITableViewCell smoothly without dismissing keyboard
我在 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];
如果我这样做,单元格会正确调整大小,但 tableView 会一直向下滚动。
我知道这种调整 UITableViewCell 大小的方法是异步工作的,这就是调用 setContentOffset 不起作用的原因,可能是因为 setContentOffset 是在 table 视图单元格大小调整处理过程中由 iOS 处理的。
我尝试了以下操作,table视图最终滚动到所需位置。但是用户可以看到 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个非常重要的细节:
- 在不关闭键盘的情况下调整单元格大小的方式。 (正常重新加载会隐藏键盘)。
- 只有在内容偏移动画完成后才能设置单元格高度。 (否则 setContentOffset 可能会被忽略)。
- 为 table 视图设置底部插图。 (否则调整单元格大小可能会向下滚动 table,隐藏我们希望在键盘上方可见的单元格。)
此解决方案已在 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];
}];}
非常重要:键盘关闭后,平滑地重新调整table视图滚动(如有必要):
- (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;
希望对你有用。
我在 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];
如果我这样做,单元格会正确调整大小,但 tableView 会一直向下滚动。 我知道这种调整 UITableViewCell 大小的方法是异步工作的,这就是调用 setContentOffset 不起作用的原因,可能是因为 setContentOffset 是在 table 视图单元格大小调整处理过程中由 iOS 处理的。
我尝试了以下操作,table视图最终滚动到所需位置。但是用户可以看到 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个非常重要的细节:
- 在不关闭键盘的情况下调整单元格大小的方式。 (正常重新加载会隐藏键盘)。
- 只有在内容偏移动画完成后才能设置单元格高度。 (否则 setContentOffset 可能会被忽略)。
- 为 table 视图设置底部插图。 (否则调整单元格大小可能会向下滚动 table,隐藏我们希望在键盘上方可见的单元格。)
此解决方案已在 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];
}];}
非常重要:键盘关闭后,平滑地重新调整table视图滚动(如有必要):
- (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;
希望对你有用。