尝试移动UITableView行时崩溃

时间:2009-12-10 22:20:19

标签: ios iphone cocoa-touch uitableview

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

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

我正在使用:

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

指定允许用户根据单元格的位置拖动行的位置。 98%的工作时间。但在某些情况下,当用户只允许在各部分之间拖动时(无法对部分内的行重新排序)会出现此错误,然后在没有行的区域滚动浏览器后应用程序崩溃。

抛出的异常非常无用:

  

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

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

-[UITableView(UITableViewInternal) _visibleCellForGlobalRow:]

有人看过这个问题吗?有什么想法吗?

7 个答案:

答案 0 :(得分:3)

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

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

如果我滚动视图以便第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,但这需要对我的数据模型进行一些复杂而麻烦的更改。

答案 1 :(得分:1)

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

这通常在代码尝试执行时发生:

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

因为count是上边界,数组从0开始,最后一个元素是count - 1

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

答案 2 :(得分:1)

我有一个类似的错误,删除了我暂时无法弄清楚 - 但我在代码周围加了[tableView beginUpdates][tableView endUpdates]并修复了所有内容。可能是您的数据源在尝试重绘之前没有更新,这些方法应该可以防止这种情况(无论如何都值得一试)。

答案 3 :(得分:1)

您是否正在更新targetIndexPathForMoveFromRowAtIndexPath

中表格的数据模型

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

从iPhone OS的表格视图编程指南中,在重新排序表格单元格下:

  

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

对于委托方法,它写成:

  

每次拖动的行都在a上   目的地,表视图发送   的的tableView:targetIndexPathForMoveFromRowAtIndexPath:toProposedIndexPath:   到它的代表(如果它实现了   方法)。在这个方法中代表   可以拒绝当前目的地   拖动的行并指定一个   另一种。

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

中进行

也许这就是你正在做的事情,但我不能仅从你提供的信息中说出来。

答案 4 :(得分:1)

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

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

答案 5 :(得分:0)

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

答案 6 :(得分:0)

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

我还在研究这个问题,但截至目前,我已禁止在iOS 3.0设备上移动行,直到找到修复程序。