在自动调整 TextView 大小时输入文本时 UITableView 闪烁/口吃

Posted

技术标签:

【中文标题】在自动调整 TextView 大小时输入文本时 UITableView 闪烁/口吃【英文标题】:UITableView flickers/stutters while entering text in auto resizing TextView 【发布时间】:2014-12-13 15:51:45 【问题描述】:

目前我有一个UITableView,其中包含一个调整大小的UITextView。单元格正在使用beginUpdates/endUpdates 自动调整大小,但是当它这样做时,表格视图会卡住(参见下面的 gif)。

最终结果是UITableViewCell,其中有一个文本视图,可以根据其内容调整大小。下面是自定义 UITableViewCell 类中的代码,它会导致 UITableView 自我更新。

- (void)textViewDidChange:(UITextView *)textView 
    // This is a category on UITableViewCell to get the [self superView] as the UITableView
    UITableView *tableView = [self tableView];
    if (tableView)
        [tableView beginUpdates];
        [tableView endUpdates];
    

以下是我已经尝试过的事情:

获取当前的contentOffset 并在endUpdates 之后重置它但没有用 在更新之前禁用UITableView 上的滚动,然后再启用 我尝试总是从- (BOOL)textViewShouldEndEditing:(UITextView *)textView 返回NO 我的UITableView 单元格高度使用UITableViewAutomaticDimension。欢迎任何其他想法或想法。

这是它的外观示例:

我不打算使用任何库,因此请不要对此提出任何建议。

谢谢

编辑:解决方案

在这里找到:I do not want animation in the begin updates, end updates block for uitableview?

感谢@JeffBowen 找到了一个很棒的发现(虽然它是可行的,但我仍然可以实现 UITableViewDelegate 方法来支持 ios 7)。在执行更新之前关闭动画,然后在更新后启用以防止 UITableView 卡顿。

[UIView setAnimationsEnabled:NO];
[tableView beginUpdates];
[tableView endUpdates];
[UIView setAnimationsEnabled:YES];

如果您不需要使用 Delegate 方法并且只希望为 iOS 8+ 提供一个不那么 hacky 的解决方案,那么请在下面使用@Massmaker 的答案。

【问题讨论】:

知道了...所以每次更新时表格都会向下移动...这是问题吗? 但是你在 beginUpdates 和 endUpdates 之间什么都没有...你没有插入、删除或选择作为 beginUpdates 和 endUpdates 的目的。 如果您将这些行更改为[tableView reloadData]; 同样的问题会发生什么? reloadData 将不起作用,因为这将导致 TextView 失去焦点。开始/结束更新允许单元格在保持焦点的同时更新其高度。似乎有一些偏移量导致tableview没有意识到有键盘并在没有键盘的情况下重置为它的偏移量。 但是您还没有指定要更新的单元格... 【参考方案1】:

在调用beginUpdates之前禁用动画,在调用endUpdates之后重新启用它。

[UIView setAnimationsEnabled:NO];
[tableView beginUpdates];
[tableView endUpdates];
[UIView setAnimationsEnabled:YES];

绝对是一个 hack,但现在可以使用。感谢我的朋友 Beau,他将我指向 this。

【讨论】:

【参考方案2】:

我的解决方案(适用于 iOS 8)首先设置在我的 viewController viewDidLoad

self.tableView.rowHeight = UITableViewAutomaticDimension;
// this line is needed to cell`s textview change cause resize the tableview`s cell
self.tableView.estimatedRowHeight = 50.0;

那么,结合这篇文章solution in Swift和一些肮脏的想法 我在我的单元格中设置了一个属性,称为

@property (nonatomic, strong) UITableView *hostTableView;

在单元格中-(void) textViewDidChange:(UITextView *)textView

 CGFloat currentTextViewHeight = _textContainer.bounds.size.height;
CGFloat toConstant = ceilf([_textContainer sizeThatFits:CGSizeMake(_textContainer.frame.size.width, FLT_MAX)].height);
if (toConstant  >  currentTextViewHeight)

    [_hostTableView beginUpdates];
    [_hostTableView endUpdates];

然后在视图控制器中

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

textCell.hostTableView = self.tableView;

【讨论】:

我正在做你所描述的事情。细微差别。我使用 tableview 的委托函数 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 和估计 - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath 相同。我没有将表格视图分配给单元格,而是从单元格中获取它,因为它是使用类别的父视图。当您调整大小的 textview 在列表中更靠后时会发生什么(即在上方添加 10 行,然后重试调整大小的行作为第 11 个元素)? 目的是不要使用这些方法,因为 iOS 8 cells auto height, depending on contents. Otherwise Id 使用您提到的传统方法。感谢您的评论)。 疯了方法)!我对此唯一担心的是,有时我需要我的行实际返回一个特定的高度(即支持 iOS7)......你知道我能做些什么来允许它吗? 再一次。如果您支持 8 之前的 iOS,您将需要这些方法。在 iOS 8 中,Apple 对单元格显示机制进行了一些更改,其中涉及自动布局和单元格标签或 textViews 或其他可能具有固有大小的内容的依赖关系。换句话说,现在单元格比托管表格视图拥有更多的权力,反之亦然。 您可能想让hostTableView 弱而不是强。这不会导致保留周期吗?【参考方案3】:

我找不到实现它的方法,因为: 当触发 [tableView endUpdates] 时,table 会重新计算 contentSize 并重新设置它。这会导致将 contentOffset 重置为默认值。

这是从 UIScrollView 继承的行为,我试图通过以下方式避免它:

继承 UITableView 并覆盖 setContentOffset:setContentOffset:animated 函数。但是当表格视图更改 contentSize 时,它​​们不会调用。 在内容大小更新后继承 UITableView,覆盖 setContentSize: 函数并将 contentOffset 设置为旧值,但它不适用于我 使用 KVO,并在 contentOffset 重置后立即为其设置旧值,但无论如何我都有这个动画问题 将 scrollEnabled 设置为 NO 并将 scrollToTop 设置为 NO,但这也无济于事

如果有人找到此问题的解决方案,欢迎。 也许可能的解决方案 - 禁用自动布局:iOS: setContentSize is causing my entire UIScrollView frame to jump

更新:我找到解决方案:直接更改单元格高度、内容大小和内容偏移量: 这对我有用(表格视图是单元格 UITextView 的代表)

- (void)textViewDidChange:(UITextView *)textView

    CGFloat textHeight = [textView sizeThatFits:CGSizeMake(self.width, MAXFLOAT)].height;
    if (self.previousTextHeight != textHeight && self.previousTextHeight > 0) 
        CGFloat difference = textHeight - self.previousTextHeight;
        CGRect cellFrame = self.editedCell.frame;
        cellFrame.size.height += difference;
        self.editedCell.frame = cellFrame;
        self.contentSize = CGSizeMake(self.contentSize.width, self.contentSize.height + difference);
        self.contentOffset = CGPointMake(self.contentOffset.x, self.contentOffset.y + difference);
    
    self.editedNote.comments = textView.text;

【讨论】:

我使用 iOS 8.0+ 进行版本控制,因此对于表格高度,我使用的是 UITableViewAutomaticDimension,因此不需要重新计算。然而,我确实添加了 - (CGFloat)tableView:(UITableView *)tableView estimateHeightForRowAtIndexPath:(NSIndexPath *)indexPath 并返回了任意行高,表格仍然像 gif 中一样口吃,除了 textview 完全进入视图(在键盘上方) 而不是呆在键盘后面,所以我明天要玩更多。 我已经检查过了,由于 UIScrollView 的限制,我们似乎无法实现它。查看更新的答案 会有类似这项工作的东西吗?我不太明白如何获得他们解释尝试使用的“verticalGap”值。 ***.com/questions/18775294/… 在这种情况下,您无法在显示键盘时在表格中滚动 高度不是UITableViewCell上的可分配值【参考方案4】:

在 Textview 值更改委托中,使用此代码调整特定单元格的大小,而不会出现任何闪烁。 但在此之前,请确保您使用了动态表格视图单元格高度。

UIView.setAnimationsEnabled(false)
DispatchQueue.main.async 
   self.tableView.beginUpdates()
   self.tableView.endUpdates()
   self.tableView.layer.removeAllAnimations()

UIView.setAnimationsEnabled(true)

【讨论】:

以上是关于在自动调整 TextView 大小时输入文本时 UITableView 闪烁/口吃的主要内容,如果未能解决你的问题,请参考以下文章

GDK Card 类中使用的可自动调整大小的 TextView 在哪里?

textView自动调整文本以填充宽度

Android 自定义TextView实现文本内容自动调整字体大小以适应TextView的大小

Angular - Textarea 自动高度不会在文本删除时自动调整大小

自动调整 Textview Android

在 ConstraintLayout 中自动调整多行文本视图的大小