首页 文章

有没有办法更新单个UITableViewCell的高度,而无需重新计算每个单元格的高度?

提问于
浏览
43

我有一个UITableView,有几个不同的部分 . 一个部分包含将在用户将文本键入UITextView时调整大小的单元格 . 另一部分包含呈现HTML内容的单元格,计算高度相对较高 .

现在当用户输入UITextView时,为了让表视图更新单元格的高度,我打电话

[self.tableView beginUpdates];
[self.tableView endUpdates];

但是,当我真的只需要更新输入的 single 单元格时,这会导致表重新计算表中 every 单元格的高度 . 不仅如此,而不是使用 tableView:estimatedHeightForRowAtIndexPath: 重新计算估计的高度,它为每个单元格调用 tableView:heightForRowAtIndexPath: ,即使是那些未显示的单元格 .

有没有办法让表格视图更新单个单元格的高度,而不做所有这些不必要的工作?

Update

我_1148476已尝试使用 reloadRowsAtIndexPaths: ,但它看起来不会起作用 . 即使只有一行调用 reloadRowsAtIndexPaths: 仍然会导致每行调用 heightForRowAtIndexPath: ,即使只为您请求的行调用 cellForRowAtIndexPath: . 实际上,看起来每次插入,删除或重新加载行时,都会为表格单元格中的每一行调用 heightForRowAtIndexPath: .

我还尝试在 willDisplayCell:forRowAtIndexPath: 中放置代码来计算单元格出现之前的高度 . 为了使其工作,我需要强制表视图在我进行计算后重新请求行的高度 . 不幸的是,从 willDisplayCell:forRowAtIndexPath: 调用 [self.tableView beginUpdates]; [self.tableView endUpdates]; 会导致UITableView 's internal code. I guess they don'中的索引超出范围异常,我们希望我们这样做 .

我可以在SDK中发现一个错误,它响应 [self.tableView endUpdates] 它没有为那些仍然试图找到某种解决方法的单元调用 estimatedHeightForRowAtIndexPath: . 任何帮助表示赞赏 .

8 回答

  • 0

    我有一个类似的问题(任何变化的tableview的跳动卷轴),因为我有

    (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {return 500; }

    评论整个功能有帮助 .

  • 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,表示没有估计值 .

  • 8

    此错误已在iOS 7.1中修复 .

    在iOS 7.0中,似乎没有任何解决此问题的方法 . 调用 [self.tableView endUpdates] 会导致为表中的每个单元调用 heightForRowAtIndexPath: .

    但是,在iOS 7.1中,调用 [self.tableView endUpdates] 会导致 heightForRowAtIndexPath: 为可见单元格调用,而 estimatedHeightForRowAtIndexPath: 将调用非可见单元格 .

  • 0

    可变行高会对表视图性能产生非常不利的影响 . 您正在谈论在某些单元格中显示的Web内容 . 如果我们不是在谈论成千上万的行,那么使用UIWebView而不是UITableView来实现您的解决方案可能是值得考虑的 . 我们有类似的情况,并使用自定义生成的HTML标记的UIWebView,它工作得很漂亮 . 您可能知道,当您拥有包含Web内容的动态单元格时,您会遇到令人讨厌的异步问题:

    • 设置完所需的单元格内容后

    • 等到单元格中的Web视图完成呈现Web内容,

    • 然后你必须进入UIWebView并使用JavaScript - 询问HTML文档有多高

    • 然后更新UITableViewCell的高度 .

    没有任何乐趣,并且为用户提供了大量的跳跃和抖动 .

    如果你必须使用UITableView,绝对是 cache 计算的行高 . 这样在 heightForRowAtIndexPath: 返回它们会很便宜 . 而不是告诉UITableView做什么,只需快速创建数据源 .

  • 22

    有办法吗?答案是不 .

    您只能使用heightForRowAtIndexPath . 因此,您所能做的就是尽可能降低成本,例如在数据模型中保留单元格高度的NSmutableArray .

  • 2

    使用以下UITableView方法:

    - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
    

    您必须指定要重新加载的NSIndex的NSArray . 如果只想重新加载一个单元格,则可以提供仅包含一个NSIndexPath的NSArray .

    NSIndexPath* rowTobeReloaded = [NSIndexPath indexPathForRow:1 inSection:0];
    NSArray* rowsTobeReloaded = [NSArray arrayWithObjects:rowTobeReloaded, nil];
    [UITableView reloadRowsAtIndexPaths:rowsTobeReloaded withRowAnimation:UITableViewRowAnimationNone];
    
  • 2

    始终会调用 heightForRowAtIndexPath: 方法,但这是我建议的解决方法 .

    每当用户输入 UITextView 时,保存_1545010_的 indexPath 的局部变量 . 然后,当调用 heightForRowAtIndexPath: 时,验证保存的 indexPath 的值 . 如果保存的 indexPath 不是 nil ,则检索应调整大小的 cell 并执行此操作 . 对于其他单元格,请使用缓存值 . 如果保存的 indexPathnil ,请执行您需要的常规代码行 .

    以下是我推荐的方法:

    使用 UITextView 属性 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...
        }
    }
    

    我希望这有帮助!祝好运 .

  • 1

    我最终找到了解决问题的方法 . 我能够预先计算我需要渲染的HTML内容的高度,并包括高度以及数据库中的内容 . 这样,虽然当我更新任何单元格的高度时,我仍然被迫为所有单元格提供高度,但我不需要做任何昂贵的HTML渲染,所以它非常活泼 .

    不幸的是,只有在您预先获得所有HTML内容的情况下,此解决方案才有效 .

相关问题