insertRowsAtIndexPaths 为每一行调用 cellForRowAtIndexPath

Posted

技术标签:

【中文标题】insertRowsAtIndexPaths 为每一行调用 cellForRowAtIndexPath【英文标题】:insertRowsAtIndexPaths calling cellForRowAtIndexPath for every row 【发布时间】:2011-05-20 21:47:16 【问题描述】:

我正在尝试使用 insertRowsAtIndexPaths 在一个步骤中将一堆行插入一个空的 UITableView。 (我知道这听起来没什么用,但它是我真正需要做的事情的简化版本。)

我看到的问题是,在我调用 insertRowsAtIndexPaths 之后,我会收到针对我插入的每一行的 cellForRowAtIndexPath 调用,而不仅仅是那些可见的。

这似乎不对。如果它这样做,对我来说几乎没用。

我看到的唯一有点奇怪的其他工件是,当我这样做时,它实际上似乎使行动画到位。这不是 withRowAnimation,因为它设置为 none。但似乎这里有某种更高级别的动画概念。我的想法是,当它动画行到位时,它认为它需要更多/所有行的单元格,直到它们被推离屏幕。但是如果不使用我希望避免的 reloadData,我就无法关闭此动画。

通常我有一个在幕后发生变化的数据集。我可以仔细检查并构建更改以生成用于插入/删除/重新加载调用的数据。但我不能将其限制为屏幕上的内容,因为它与数据提供者不匹配。

我想我一定是遗漏了什么……有什么想法吗?

(我在 beginUpdates/endUpdates 对中执行此操作,但这似乎没有区别。)

【问题讨论】:

听起来像一个错误。提交错误报告:bugreport.apple.com 你能创建一个简单的项目/代码,我们可以测试它来复制行为吗?听起来确实可能是一个错误。 是的,如果不知道的话,我想这是下一步。 @smparkes 提交报告后,请在此处留下错误编号的评论。 :) @Dave DeLong,每个人都这么说,但在雷达上,我永远无法弄清楚如何引用另一个错误。任何的想法?或者只是将 # 留在正文中? 【参考方案1】:

我今天与一些 Apple 人员进行了交谈。这个问题肯定与插入新行时 UITableView 所做的动画有关。这是为单元格创建新行的动画,与通过 insertRowsAtIndexPaths 引入这些行时使用的动画不同。

由于表格级动画通过一种“向下增长”的方式创建新行,因此在插入动画期间,许多单元格被认为是可见的,表格将为它们调用 cellForRowAtIndexPath。

似乎没有任何替代实现适用于所有情况。我的案例推荐的解决方案是“撒谎”:找出动画后页面上将存在哪些单元格并插入这些单元格。动画完成后,插入剩余的不会显示的单元格,表格不会调用 cellForRowAtIndexPath。

这需要在表格视图和“真实”数据源之间实现一个对象,该对象可以向表格视图“谎报”动画期间表格中的行数。很痛苦,但这显然是在类似情况下所做的。

【讨论】:

“一种“成长”” - 听起来您使用的是 UITableViewRowAnimationTop 或 UITableViewRowAnimationBottom。您可以尝试使用 -right、-left 或 -fade 动画,这样一开始并不是所有单元格都可见。 不。这不是单元格动画。这是表格级动画(Apple 确认),即使您使用 UITableViewRowAnimationNone 也会发生:表格仍然会为单元格设置动画,至少在表格中没有要开始的行的情况下,只有空占位符。禁用此功能的唯一方法是在 UIView 级别关闭动画,但随后您几乎又回到了 reloadData。 我今天遇到了这个问题再次,完全忘记了这篇文章,重新用谷歌搜索它,天哪..为什么没有解决这个问题。 【参考方案2】:

我遇到了同样的问题,我所做的是声明一个名为 bMassiveLoad 的属性初始化为 false。 在执行“大量加载”之前,我将其设置为 true,并且我在 cellForRowAtIndexPath 中做的第一件事是检查该属性。 在“大规模加载”结束时,我再次将其设置为 false 并调用 reloadData...

我知道这不是一个漂亮的答案,但它对我有帮助。

【讨论】:

这基本上也是我所做的。当表格尝试分配/初始化一百多个表格单元格时,即时 reloadData 胜过将 UI 挂起半秒。【参考方案3】:

我可以通过结合这里的一些答案来解决这个问题:

按照 pocjoc 的建议根据需要设置大量负载布尔值

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath 
    UITableView *tableView = self.tableView;
    switch(type) 
        case NSFetchedResultsChangeInsert:
            massiveLoad = YES;
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
        ...
    

并在结束时重新加载可见行

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller 
    [self.tableView endUpdates];
    if (massiveLoad) 
        massiveLoad = NO;
        [self.tableView reloadRowsAtIndexPaths:[self.tableView indexPathsForVisibleRows] withRowAnimation:UITableViewRowAnimationFade];
    

【讨论】:

【参考方案4】:

UITableView 在调用 insertRowsAtIndexPaths 后立即调用相关的委托和数据源方法以获取可见单元格的单元格和其他内容。这与动画是否设置为 YES 或 NO 无关。

查看讨论here

如果您只想让 tableView 更新可见单元格,那么只需更新您的 dataSource 并调用 [tableView reloadData]。

【讨论】:

我的问题是它为不可见的单元格调用 cellForRowAtIndexPath ,至少在插入动画完成后。 (而且我不确定你在说什么动画:是/否。)【参考方案5】:

我不知道你描述的行为是否正确,但UITableView 有两种方法:beginUpdatesendUpdates (here) 旨在优化重绘(因此只进行动画最后,否则它们会被一一完成,这可能会产生对cellForRowAtIndexPath的调用。

现在,我不确定这是否可以帮助您,但也许您可以尝试一下...

【讨论】:

我忘了提它,但我是在开始/结束对中执行此操作的。不过好像没什么区别。【参考方案6】:

我觉得可能有不同的方法可以解决这个问题,但我发现如果您在可见范围内有现有单元格(例如,如果您有 10 个始终可见的单元格并且您初始化tableView 有 10 个空白单元格),当您添加新行时,cellForRowAtIndexPath 中不会调用额外的行。

那么,该怎么办呢。这就是我解决这个问题的方法。当我创建 tableView 时,我将单元格添加到可见区域。他们一片空白。然后,当我插入新行时,我只为额外的行添加了索引路径(即,如果我有 10 个可见行并且想要添加 50 个行,包括可见行,我只添加了 40 个,因为前 10 个已经存在)。这当然会留下 10 个空白单元格。此时,您可以使用:

NSArray *indexPaths = [myTableView indexPathsForVisibleCells];
[myTableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];

这会选择您的 10 个可见空白单元格并使用您传入的新信息重新加载它们(我假设您为此部分将数据加载到数组中)。您需要在 cellForRowAtIndexPath 方法中添加一些逻辑,例如:

if (![myDataArray count]) 
    // create a blank cell;
 else 
    // add context to your cells as you want;

这应该可以为您解决问题,并且只加载您在屏幕上显示的单元格

【讨论】:

我需要添加单元格。并非所有内容都可见。 哦,我明白了。我很抱歉造成混乱。我将编辑我的答案来解决这个问题 我真的不需要更新任何东西。我只想插入 50 行,但只在显示的 10 行上调用 cellForRowAtIndexPath。您是否建议调用 reload* 而不是 insert*?我还没有尝试过(还),但这听起来像是要求它重新加载不存在的行。 对不起,我以为你说的是​​相反的,你已经有一堆行但只想更新可见的行。所以你想添加一堆,但只显示几个? 我只是想在插入的上下文中获得正常的单元格渲染。当您执行 reloadData 时,表视图只为屏幕上的行调用 cellForRowAtIndexPath。当我插入行时,它会为我插入的每一行调用 cellForRowAtIndexPath,无论它们是否显示。太贵了。【参考方案7】:

为了快速,你可以试试这个

self.mainTableView.beginUpdates()
    self.mainTableView.insertRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.Fade)
    self.mainTableView.reloadRowsAtIndexPaths(self.mainTableView.indexPathsForVisibleRows!, withRowAnimation: UITableViewRowAnimation.Fade)
    self.mainTableView.endUpdates()

【讨论】:

【参考方案8】:

UITableView 会通过cellForRowAtIndexPath: 方法请求可见单元格,并且可以减少单元格很多时的处理时间,例如1k 单元格,并通过可重用单元格机制减少消耗的内存。如果您需要处理所有单元格,那么您应该实现一个自定义方法来重新处理您的源数据,然后调用[tableView reloadData] 来更新 UI。

【讨论】:

以上是关于insertRowsAtIndexPaths 为每一行调用 cellForRowAtIndexPath的主要内容,如果未能解决你的问题,请参考以下文章

insertRowsAtIndexPaths 自定义动画 Swift

insertRowsAtIndexPaths:没有 reloadRowsAtIndexPaths 就不会动画:

带有 2 个动画的 insertRowsAtIndexPaths

insertRowsAtIndexPaths: with scrollToRowAtIndexPath: 导致 UITableView 部分被错误地隐藏

如何使用 insertRowsAtIndexPaths 将数组内的字典添加到 UItableview?

使用 insertRowsAtIndexPaths 时空数组的索引超出范围