在自动调整 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 I
d 使用您提到的传统方法。感谢您的评论)。
疯了方法)!我对此唯一担心的是,有时我需要我的行实际返回一个特定的高度(即支持 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 在哪里?
Android 自定义TextView实现文本内容自动调整字体大小以适应TextView的大小