我有一个带有几个不同部分的UITableView。一个部分包含将在用户将文本键入UITextView时调整大小的单元格。另一部分包含呈现HTML内容的单元格,计算高度相对较贵。
现在当用户输入UITextView时,为了让表视图更新单元格的高度,我调用
[self.tableView beginUpdates];
[self.tableView endUpdates];
然而,当我真的只需要更新输入的单个单元格时,这会导致表格重新计算表格中每个单元格的高度。不仅如此,而不是使用tableView:estimatedHeightForRowAtIndexPath:
重新计算估计的高度,它会为每个单元格调用tableView:heightForRowAtIndexPath:
,甚至是那些未显示的单元格。
有没有办法让表格视图更新单个单元格的高度,而不进行所有这些不必要的工作?
更新
我仍在寻找解决方案。正如所建议的那样,我已经尝试过使用reloadRowsAtIndexPaths:
,但看起来这不会起作用。即使只有一行调用reloadRowsAtIndexPaths:
仍然会导致每行调用heightForRowAtIndexPath:
,即使只为您请求的行调用cellForRowAtIndexPath:
。实际上,看起来每次插入,删除或重新加载行时,都会为表格单元格中的每一行调用heightForRowAtIndexPath:
。
我还尝试在willDisplayCell:forRowAtIndexPath:
中放置代码来计算单元格出现之前的高度。为了使其工作,我需要强制表视图在我进行计算后重新请求行的高度。不幸的是,从[self.tableView beginUpdates]; [self.tableView endUpdates];
调用willDisplayCell:forRowAtIndexPath:
会导致UITableView内部代码中的索引超出范围异常。我猜他们不指望我们这样做。
我不禁觉得这是SDK中的一个错误,响应[self.tableView endUpdates]
它没有为不可见的单元格调用estimatedHeightForRowAtIndexPath:
,但我仍在尝试找到某种解决方法。任何帮助表示赞赏。
答案 0 :(得分:29)
如上所述,reloadRowsAtIndexPaths:withRowAnimation:
只会使表格视图向其UITableViewDataSource
询问新的单元格视图,但不会要求UITableViewDelegate
更新单元格高度。
不幸的是,只有通过调用
才能刷新高度[tableView beginUpdates];
[tableView endUpdates];
即使两次通话之间没有任何变化。
如果您计算高度的算法太耗时,您应该缓存这些值。 类似的东西:
- (CGFloat)tableView:(UITableView *)tableView
heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat height = [self cachedHeightForIndexPath:indexPath];
// Not cached ?
if (height < 0)
{
height = [self heightForIndexPath:indexPath];
[self setCachedHeight:height
forIndexPath:indexPath];
}
return height;
}
确保在内容更改时或在初始时将这些高度重置为-1
。
编辑:
此外,如果您想尽可能延迟高度计算(直到它们滚动到),您应该尝试实现此功能(仅限iOS 7+):
@property (nonatomic) CGFloat estimatedRowHeight
提供行高的非负估计可以改善行 加载表视图的性能。如果表包含变量 高度行,计算所有高度时可能会很昂贵 桌子加载。使用估算可以让您推迟一些成本 从加载时间到滚动时间的几何计算。
默认值为0,表示没有估算值。
答案 1 :(得分:23)
此错误已在iOS 7.1中修复。
在iOS 7.0中,似乎没有办法解决这个问题。调用[self.tableView endUpdates]
会导致为表中的每个单元调用heightForRowAtIndexPath:
。
但是,在iOS 7.1中,调用[self.tableView endUpdates]
会导致为heightForRowAtIndexPath:
调用可见单元格,并为不可见单元格调用estimatedHeightForRowAtIndexPath:
。
答案 2 :(得分:8)
可变行高会对您的表格视图性能产生非常不利的影响。您正在谈论在某些单元格中显示的Web内容。如果我们不是在谈论成千上万的行,考虑使用UIWebView而不是UITableView实现您的解决方案可能值得考虑。我们遇到了类似的情况,并使用带有自定义生成的HTML标记的UIWebView,它工作得很漂亮。您可能知道,当您拥有包含Web内容的动态单元格时,您会遇到令人讨厌的异步问题:
没有任何乐趣,并且为用户提供了大量的跳跃和抖动。
如果你必须使用UITableView,绝对缓存计算的行高。这样,在heightForRowAtIndexPath:
中返回它们会很便宜。而不是告诉UITableView做什么,只需快速创建数据源。
答案 3 :(得分:2)
有办法吗? 答案是否定的。
您只能使用heightForRowAtIndexPath。 因此,您所能做的就是尽可能降低成本,例如在数据模型中保留单元格高度的NSmutableArray。
答案 4 :(得分:2)
我有一个类似的问题(在任何变化时跳过tableview的滚动)因为我有
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { 返回500; }
评论整个功能有帮助。
答案 5 :(得分:1)
使用以下UITableView方法:
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
您必须指定要重新加载的NSIndexPath的NSArray。如果只想重新加载一个单元格,则可以提供仅包含一个NSIndexPath的NSArray。
NSIndexPath* rowTobeReloaded = [NSIndexPath indexPathForRow:1 inSection:0];
NSArray* rowsTobeReloaded = [NSArray arrayWithObjects:rowTobeReloaded, nil];
[UITableView reloadRowsAtIndexPaths:rowsTobeReloaded withRowAnimation:UITableViewRowAnimationNone];
答案 6 :(得分:0)
方法heightForRowAtIndexPath:
将始终被调用,但这是我建议的解决方法。
每当用户输入UITextView
时,请在本地变量中保存单元格的indexPath
。然后,在调用heightForRowAtIndexPath:
时,验证已保存的indexPath
的值。如果保存的indexPath
不是nil
,请检索应调整大小的单元格并执行此操作。对于其他单元格,请使用缓存的值。如果保存的indexPath
为nil
,请执行您需要的常规代码行。
以下是我推荐的方法:
使用tag
的属性UITextView
来跟踪需要调整大小的行。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
...
[textView setDelegate:self];
[textView setTag:indexPath.row];
...
}
然后,在UITextView
委托方法textViewDidChange:
中,检索indexPath
并存储它。 savedIndexPath
是一个局部变量。
- (void)textViewDidChange:(UITextView *)textView
{
savedIndexPath = [NSIndexPath indexPathForRow:textView.tag inSection:0];
}
最后,检查savedIndexPath
的值并执行它所需的内容。
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (savedIndexPath != nil) {
if (savedIndexPath == indexPath.row) {
savedIndexPath = nil;
// return the new height
}
else {
// return cached value
}
}
else {
// your normal calculating methods...
}
}
我希望这有帮助!祝你好运。
答案 7 :(得分:0)
我最终找到了解决问题的方法。我能够预先计算我需要渲染的HTML内容的高度,并包括高度以及数据库中的内容。这样,虽然当我更新任何单元格的高度时,我仍然被迫为所有单元格提供高度,但我不需要做任何昂贵的HTML渲染,所以它非常活泼。
不幸的是,只有在您预先获得所有HTML内容的情况下,此解决方案才有效。