尝试移动 UITableView 行时崩溃

Posted

技术标签:

【中文标题】尝试移动 UITableView 行时崩溃【英文标题】:Crashing while trying to move UITableView rows 【发布时间】:2009-12-10 22:20:18 【问题描述】:

我有一些相当复杂的规则来移动UITableView 中的行。每个部分有未定义的部分和行数,并且根据各种规则,用户可以在部分内或部分之间将行移动到特定的其他位置。

所有数据都在更新,一切正常。但偶尔,在移动一行后,应用程序会假发,突然会出现一个空白区域,应该显示一行。

我正在使用:

              - (NSIndexPath *)tableView:(UITableView *)tableView
targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath

根据单元格的位置指定允许用户拖动行的位置。 98% 的时间它都在工作。但在某些情况下,当用户只允许在部分之间拖动(不能重新排列部分内的行)时,会出现此错误,然后应用在滚动到没有行的区域后崩溃。

抛出的异常没什么用:

由于未捕获的异常'NSRangeException'而终止应用程序,原因:'*** -[NSCFArray objectAtIndex:]:索引(6)超出范围(6)

我的代码都没有在堆栈上。最后一个 UITableView 特有的方法是

-[UITableView(UITableViewInternal) _visibleCellForGlobalRow:]

以前有人见过这个问题吗?有什么想法吗?

【问题讨论】:

你解决过这个 Ed 吗?没有标记为正确的答案,我也看到了同样的问题。 不,我从来没有解决过这个问题。我不得不放宽可以将一行拖到哪里的规则,这样就不会发生 您是否在 moveRowAtIndexPath 方法中执行任何类型的重新加载操作(例如 reloadData、reloadSections 或 reloadRowsAtIndexPath)?我遇到了同样的问题,我尝试按照下面 Tom S. 的建议在延迟的 performSelector 中重新加载,它似乎有效,除了我将延迟设置为 1 秒以保持 iPhone 3.x、4 .x,模拟器很开心。可以肯定的是,这有点骇人听闻。 这可能无法解决这个问题,但我发现必须在“moveRowAtIndexPath”末尾调用“reloadData”,否则可能会引发内部不一致,特别是如果您支持轮换。 【参考方案1】:

我只是在我的应用程序中遇到了我认为相同的问题。

情况是我有两个表格部分。可以在部分内和部分之间拖动项目。用户可以将单元格拖动到第一部分中的任何行,但在第二部分中,项目是排序的,因此对于任何给定的单元格,只有一个有效行。

如果我滚动视图以使第 1 部分的底部和第 2 部分的顶部可见,请在第 1 部分中抓取一个排序到第 2 部分底部的项目,然后将其拖到第 2 部分的顶部,我的tableView:targetIndexPathForMoveFromRowAtIndexPath:toProposedIndexPath: 方法被调用并返回正确的目标位置,即屏幕底部下方的几行。在 UI 中,您可以看到在屏幕底部创建了一个空单元格,这不是正确的目标行。

当您放开该单元格时,在屏幕底部(在第 2 部分的中间)创建的那个虚假单元格仍留在那里! tableView:cellForRowAtIndexPath: 甚至从未被要求这样做。只要您尝试对该单元格执行任何操作,就会崩溃。

我的第一个解决方案是在tableView:moveRowAtIndexPath:toIndexPath: 末尾调用 [tableView reloadData]。但这会导致崩溃,所以我在延迟后间接调用它。但是还有另一个错误:在延迟的 reloadData 调用之后,tableView:moveRowAtIndexPath:toIndexPath: 再次被调用,并发出虚假请求,将项目从第一部分的末尾移到同一位置。所以,我不得不添加代码来忽略虚假的无操作请求。

所以,这里是代码:

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)pathSrc toIndexPath:(NSIndexPath *)pathDst

  // APPLE_BUG: after doing the delayed table reload (see below), we get a bogus
  // request to move a nonexistant cell to its current location
  if (pathSrc.row == pathDst.row && pathSrc.section == pathDst.section)
    return;

  // update your data model to reflect the move...

  // APPLE_BUG: if you move a cell to a row that's off-screen (because the destination
  // has been modified), the bogus cell gets created and eventually will cause a crash
  [self performSelector:@selector(delayedReloadData:) withObject:tableView afterDelay:0];


- (void)delayedReloadData:(UITableView *)tableView

  Assert(tableView == self.tableView);
  [tableView reloadData];

请注意,仍然存在 UI 错误。在屏幕上,拖动的单元格被动画化到虚假的空单元格中。在动画结束时,空单元格会使用该行的正确数据重新绘制,但细心的用户会注意到拖动的单元格被动画到错误的位置,然后立即变形到另一个单元格。

这绝对是一个愚蠢的用户界面。我考虑将正确的目标行滚动到屏幕上,但如果我这样做,它将用第二部分填充屏幕,然后任何拖回第一部分的尝试都会被我的(现在很烦人的)自动滚动不断地阻挠。我可能需要更改 UI,但这需要对我的数据模型进行一些复杂且麻烦的更改。

【讨论】:

我很高兴至少还有一个人和我有同样的经历!【参考方案2】:

我在删除时遇到了类似的错误,我有一阵子想不通——但我将[tableView beginUpdates][tableView endUpdates] 放在代码周围,它修复了所有问题。可能是您的数据源在尝试重绘之前没有更新,而这些方法应该可以防止这种情况发生(无论如何值得一试)。

【讨论】:

【参考方案3】:

似乎某处正在从一个数组中请求元素 6,该数组只有索引 0-5 处的元素(意味着 6 个元素)。

这通常发生在代码尝试执行以下操作时:

NSUInteger index = [somearray count];
id obj = [somearray objectAtIndex:index];

因为count 是上边界并且数组从0 开始,所以最后一个元素位于count - 1

这可能不会直接出现在您的代码中,但您可能会将某些内容限制为多个元素,然后在最后一个元素之后请求一个。

【讨论】:

请注意,应该使用NSUInteger 作为count 的结果,以免被截断(特别是在将代码移动到Mac 时,NSUInteger 越来越多地大于@ 987654328@)。保持它无符号也很重要,这样与这个永不负值的比较总是有意义的。 我知道基本问题,但访问数组的代码在框架内很深。就像我说的,崩溃时堆栈上绝对没有我自己的代码。从根本上讲,它似乎是 UITableView 对象中的一个错误。 我遇到了类似的问题,因为我使用了tableView:numberOfRowsInSection:的返回值,谢谢【参考方案4】:

您是否正在为targetIndexPathForMoveFromRowAtIndexPath 中的表更新数据模型

或者在 DataSource 委托方法中:tableView:moveRowAtIndexPath:toIndexPath: ?

取自 iPhone OS 的 Table View Programming Guide,在重新排序表格单元格下:

表格视图发送 tableView:moveRowAtIndexPath:toIndexPath: 到它的数据源(如果它实现 方法)。 在这个方法中数据 source 更新数据模型数组 那是物品的来源 表格视图,将项目移动到 数组中的不同位置

而对于委托方法,则是这样写的:

每次拖动的行超过一个 目的地,表格视图发送 tableView:targetIndexPathForMoveFromRowAtIndexPath:toProposedIndexPath: 给它的代表(如果它实现了 方法)。在这个方法中,委托 可能会拒绝当前目的地 拖动的行并指定一个 另一种选择。

tableView:targetIndexPathForMoveFromRowAtIndexPath:toProposedIndexPath: 用于确定是否允许重定位,但您的数据模型的实际更改应该发生在tableView:moveRowAtIndexPath:toIndexPath:

也许这就是你正在做的事情,但我无法仅从你提供的信息中判断。

【讨论】:

不,我在 moveRowAtIndexPath 方法中更新它【参考方案5】:

这里的 Cityarray 是一个数组...

   id obj =[Cityarray objectatindex:sourceindexpath.row];
        [Cityarray removeObjectatindex:(sourceindexpath.row)];
        [Cityarray insertObject:obj atindex:destinationindexpath.row];

【讨论】:

【参考方案6】:

我已经有一段时间没有解决这个问题了。我想出的解决方案是删除复杂的规则。不知道为什么他们允许使用复杂的规则,如果它在使用它们时使应用程序崩溃。不确定这是否已在最新版本的操作系统中修复。

【讨论】:

【参考方案7】:

我只是碰巧目睹了这个问题。但是,我的崩溃版本似乎发生在我试图支持的 ios 3.0 设备上,并且我试图重新排列表中只有两行。我在另一台装有 iOS 4.0 的设备上使用相同的代码再次运行该应用程序,并且该错误似乎已得到修复。

我仍在研究此问题,但截至目前,我正在禁用 iOS 3.0 设备上的行移动,直到找到修复程序。

【讨论】:

以上是关于尝试移动 UITableView 行时崩溃的主要内容,如果未能解决你的问题,请参考以下文章

选择行时 UITableView 崩溃

使用 CoreData 删除行时 UITableView 崩溃

在多节 UITableView 中添加行时崩溃

将 UITableView 添加到现有项目:添加数组以填充行时崩溃

Swift-app 在 UITableView 中选择行时崩溃

UITableView 和 numberOfRowsInSection 崩溃