【问题标题】:UITableView Crashes if Data Source Is Updated During Scrolling如果在滚动期间更新数据源,UITableView 会崩溃
【发布时间】:2010-02-23 13:31:12
【问题描述】:

我有一个应用程序,其中 UITableView 的数据源由远程服务器的后台线程每 30 秒更新一次。

如果用户正在滚动 tableView 或者 tableView 正在处理reloadTableView:,则会发生崩溃。崩溃的原因是崩溃时表中的行数与重绘开始时的行数不匹配。

当请求的 TableView 单元格超出范围时会发生另一次崩溃,因为在调用 numberofTableViewCells: 和调用 cellfForRowAtIndexPath 之间,数据模型已更改并且单元格不再存在。

tableView 的数据是从后台线程更新的。从服务器加载数据时,用户应该仍然能够与 tableView 交互,但现在这会导致崩溃。

如何在更新 tableViewDataSource 时阻止 tableView 滚动或重新加载? 这种情况的最佳做法是什么?

谢谢。

【问题讨论】:

  • 有关您的数据模型的详细信息会很有用。您使用的是核心数据还是其他?
  • 另外,您使用什么方法定期重新加载 UITableView?你的后台线程没有调用 [tableView reloadData] 或任何东西,是吗?
  • 是的,我正在调用 [tableView reloadData]; (数据模型存储在 SQLIte 中。)

标签: iphone uitableview


【解决方案1】:

简短的回答是,您需要在数据模型中缓冲数据,并且仅在表格未滚动时使用新数据更新表格。这需要在数据模型中完成,因为数据源委托不知道数据模型内部是什么或发生了什么变化。

但是,从 UI 设计的角度来看,在用户滚动时主动更新表格会使用户迷失方向。用户会认为他们在表格的顶部/中间/底部,然后突然发现自己在底部/顶部/中间。用户会认为他们已经查看了表格某一部分中的所有数据,但实际上表格中添加了他们需要查看的内容。例如,如果表是按字母顺序排列的名称列表,则用户检查所有以“U”开头的名称,发现没有,然后去检查表中的其他位置。同时,当用户正在寻找其他地方时,表格会用新名称无形地更新“U”部分。即使用户知道表格是动态更新的(大多数不是),他们也必须不断滚动检查几乎整个表格以查看发生了什么变化。

更好的 UI 设计是赋予用户更新的能力。在栏中放置“更新”或“新数据可用”按钮,然后将其设置为在新数据到达时出现。单击按钮后,冻结表,更新,然后才让用户恢复交互。在视觉上标记添加的行也是一个很好的设计。

这将使用户界面更易于理解,同时解决您的崩溃问题。

编辑01:

如果你不使用 Core Data,为了实现表的实时和不可见的更新。在调用– tableView:cellForRowAtIndexPath: 之前,您需要冻结调用– tableView:numberOfRowsInSection– numberOfSectionsInTableView:

由于首先调用– tableView:numberOfRowsInSection,我会在那里调用以首先更新然后冻结您的数据模型。这样,数据模型将返回正确数量的部分和行。

我认为您必须将数据模型分为两部分,其中一个将缓冲传入的数据,另一个将排序数据以供显示。您的更新方法应该将所有最终缓冲的数据移动到显示数据部分。

此外,您可能需要为用户不移动桌子时设置一个计时器。如果表没有被主动操作,计时器应该调用更新方法,然后它应该强制更新。

如果你使用Core Data,你可以使用NSFetchedResultController,它的委托方法会在数据模型改变时通知你。它应该返回实时更新的正确部分和行信息。以这种方式驱动更新表非常容易。但是,它不会克服数据进入模型太快以致模型在方法调用之间发生变化的问题。您仍然需要冻结和/或减慢模型。但是,您将不需要计时器。

Core Data 是您的最佳选择,但即便如此,它也将难以实现,因为您正试图做一些违背 UI 的事情,因此 API 不容易支持它。

更新:

回顾这个答案,我发现我忽略了提到[UITableView beginUpdates],它会在添加或删除行时冻结表的配置。它与 [UITableView endUpdates] 配对以包含 UI 中的更改。

【讨论】:

  • 感谢您的建议,但我的应用程序的重点是它没有“重新加载”或“同步”或“更新”按钮。对于用户来说,一切都应该是透明的和幕后的。我想让它“正常工作”。
  • 它不会对用户“正常工作”,因为界面会不断发展。我强烈建议您在实施之前在一些测试用户上测试此设计。但是,请参阅我的编辑以了解实现动态更新的方式。
【解决方案2】:

从后台线程调用 GUI 对象(如 UITableView)上的方法是不安全的。

您需要执行以下操作:

[ tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO]

uitableview-drawing-problems-when-reloaddata-is-called

【讨论】:

  • OP 实际上并没有提到他们正在从后台线程更新 UITableView。
  • 我认为这是一个无关紧要的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-07-10
  • 2011-12-26
  • 1970-01-01
  • 2012-12-07
  • 1970-01-01
  • 2017-04-17
  • 1970-01-01
相关资源
最近更新 更多