有没有办法更新单个UITableViewCell的高度,而无需重新计算每个单元格的高度?
我有一个UITableView与几个不同的部分。 一个部分包含单元格,当用户将文本input到UITextView中时将调整其大小。 另一部分包含呈现HTML内容的单元格,对此,计算高度相对较为昂贵。
现在,当用户inputUITextView时,为了获得表视图来更新单元格的高度,我打电话给
[self.tableView beginUpdates]; [self.tableView endUpdates];
但是,这导致表格重新计算表格中每个单元格的高度,当我真的只需要更新input到的单个单元格。 不仅如此,而不是使用tableView:estimatedHeightForRowAtIndexPath:
来重新计算估计的高度,而是为每个单元调用tableView:heightForRowAtIndexPath:
即使那些不被显示的单元。
有没有什么办法可以让表格视图更新一个单元格的高度,而不用做所有这些不必要的工作?
更新
我仍然在寻找解决scheme。 build议,我已经尝试使用reloadRowsAtIndexPaths:
,但它看起来不会像这样工作。 调用reloadRowsAtIndexPaths:
即使单行也仍然会导致为每行调用heightForRowAtIndexPath:
即使cellForRowAtIndexPath:
只会针对您请求的行调用。 实际上,它看起来像插入,删除或重新载入行的时候, heightForRowAtIndexPath:
将针对表格单元格中的每一行调用。
我也试着把代码放在willDisplayCell:forRowAtIndexPath:
来计算单元出现之前的高度。 为了这个工作,我需要强制表视图重新请求行的高度后,我做了计算。 不幸的是,调用[self.tableView beginUpdates]; [self.tableView endUpdates];
[self.tableView beginUpdates]; [self.tableView endUpdates];
from willDisplayCell:forRowAtIndexPath:
在UITableView的内部代码中导致索引越界exception。 我猜他们不指望我们这样做。
我不禁觉得这是一个SDK中的错误,作为对[self.tableView endUpdates]
响应,它不会调用estimatedHeightForRowAtIndexPath:
对于不可见的单元格,但我仍然试图find某种解决方法。 任何帮助表示赞赏。
如上所述, reloadRowsAtIndexPaths:withRowAnimation:
只会导致表视图询问其UITableViewDataSource
新的单元格视图,但不会要求UITableViewDelegate
更新的单元格高度。
不幸的是,高度只能通过调用刷新:
[tableView beginUpdates]; [tableView endUpdates];
即使两个电话之间没有任何变化。
如果你的algorithm计算高度太费时,也许你应该caching这些值。 就像是:
- (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
提供对行高度的非负估计可以提高加载表视图的性能。 如果表格包含可变高度的行,则在表格加载时计算其所有高度可能是昂贵的。 使用估算function可以将几何计算的一些成本从加载时间推迟到滚动时间。
默认值是0,这意味着没有估计。
这个bug已经在iOS 7.1中修复了。
在iOS 7.0中,似乎没有办法解决这个问题。 调用[self.tableView endUpdates]
导致heightForRowAtIndexPath:
为表中的每个单元heightForRowAtIndexPath:
用。
但是,在iOS 7.1中,调用[self.tableView endUpdates]
导致为可见单元[self.tableView endUpdates]
heightForRowAtIndexPath:
以及为不可见单元调用estimatedHeightForRowAtIndexPath:
heightForRowAtIndexPath:
可变的行高对您的表视图性能有非常负面的影响。 您正在讨论某些单元格中显示的网页内容。 如果我们不谈论数千行,那么考虑用UIWebView而不是UITableView来实现解决scheme可能是值得考虑的。 我们有一个类似的情况,并与自定义生成的HTML标记与UIWebView,它的工作很好。 正如您可能知道的那样,当您拥有包含Web内容的dynamic单元格时,您会遇到一个讨厌的asynchronous问题:
- 设置完单元格的内容后,您必须
- 等到单元格中的Web视图完成渲染Web内容后,
- 那么你必须进入UIWebView和 – 使用JavaScript – 问HTML文件有多高
- 然后更新UITableViewCell的高度。
根本没有乐趣,也没有很多跳跃和抖动的用户。
如果您必须使用UITableView,则必须caching计算的行高。 那样的话,用heightForRowAtIndexPath:
返回它们会很便宜。 不要告诉UITableView要做什么,只要让你的数据源更快。
有没有办法? 答案是不。
你只能使用heightForRowAtIndexPath这个。 所以你所能做的只是通过例如在你的数据模型中保存一个你的单元格高度的NSmutableArray来尽可能地降低成本。
我有一个类似的问题(跳转的tableview查看任何变化的滚动),因为我有
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {return 500; }
评论整个function帮助。
使用下面的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];
方法heightForRowAtIndexPath:
将永远被调用,但这是一个解决方法,我会build议。
每当用户在UITextView
input时,保存在本地variables中的单元格的indexPath
。 然后,当调用heightForRowAtIndexPath:
时,validation保存的indexPath
的值。 如果保存的indexPath
不nil
,请检索应该resize的单元格 。 至于其他单元格,使用您的caching值。 如果保存的indexPath
是nil
,请执行您的常规代码行,这在您的情况下是要求的。
以下是我推荐的方法:
使用UITextView
的属性tag
来跟踪哪一行需要resize。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { ... [textView setDelegate:self]; [textView setTag:indexPath.row]; ... }
然后,在你的UITextView
委托的方法indexPath
textViewDidChange:
,检索indexPath
并存储它。 savedIndexPath
是一个局部variables。
- (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... } }
我希望这有帮助! 祝你好运。
我最终找出了解决这个问题的方法。 我能够预先计算我需要呈现的HTML内容的高度,并将高度和内容一起包含在数据库中。 这样,当我更新任何单元格的高度时,虽然我仍然不得不为所有单元格提供高度,但我不必做任何昂贵的HTML渲染,所以它非常快速。
不幸的是,这个解决scheme只有在您预先获得所有HTML内容的情况下才能正常工作。