在iOS 6/7中,“no index path for table cell being reused”消息的含义是什么?

58
自从开始使用iOS 6(以及iOS 7)编译我的应用程序之后,我开始看到这个消息。我知道UITableView在iOS 6中管理单元格的方式不同,但我没有必要修改我的代码让它继续工作。但我担心这个消息可能指向一些潜在问题,我还没有看到。有人能解释一下吗?
14个回答

132

在 iOS 7 beta 5 以及之后的版本,包括 iOS 7 GM/Release 版本中,我开始在日志中看到这个错误,而在我的应用程序中从未在 iOS 6 或更早的 iOS 7 beta 版本中出现过。经过许多尝试,我找到了原因:

我在我的节标题视图中使用 UITableViewCell 对象,并在 tableView:viewForHeaderInSection: 中返回它们。这似乎是常见的做法,特别是自从 iOS 5 开始,使用 Interface Builder 在 StoryBoard 中设计部分标题视图作为原型表视图单元格变得容易。

当我改用普通的 UIView 子类来做节标题视图时,错误消失了,更重要的是,我的表视图停止了随机删除节标题!

看起来(自 iOS 7 beta 5 以来),UITableView 内部维护着其视图层次结构中所有 UITableViewCell 对象及其各自的索引路径的映射关系。由于节标题(或表视图头部和尾部)没有索引路径,如果您为这些视图使用 UITableViewCell 对象,则当表视图发现一个没有索引路径的 UITableViewCell 时,它将变得混乱,导致“no index path for table cell being reused” 错误,并且,如果你不幸的话,会在你的表视图中显示故障:

更新: 如果您可以访问 Apple Dev 论坛,请参考这个主题(我发起的):https://devforums.apple.com/message/882042#882042

如该主题所建议的那样,如果您不想进行太多的重构,可以创建一个围绕着您的 UITableViewCellUIView 包装器,并将其作为节标题视图返回。

UIView *view = [[UIView alloc] initWithFrame:[cell frame]];
[view addSubview:cell];

return view;

请注意,然而这种“包装器” UIView 方法在AutoLayout和设备旋转方面并不兼容,因此建议您使用一个UIView子类来创建标头和尾部单元格,而不是像答案中的主要部分所解释的那样使用UITableViewCell子类。


3
太棒了!我也遇到了标题消失的问题。这将在未来几天内节省我很多时间。谢谢!那么你能否仍然在Storyboard中设计你的标题呢?你只需在Storyboard设置中将类更改为UIView吗? - Chris Wagner
2
@ChrisWagner 不是的,我只是为标题单元格创建了一个独立的XIB文件,然后像这样加载它:NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:@"SectionHeader" owner:self options:nil]; SectionHeaderView *sectionHeaderView = nib[0]; 据我所知,在StoryBoard中你不能创建一个不是视图控制器的独立视图。 - mluisbrown
2
@ChrisWagner 另一种看起来也行的替代方法是创建一个具有相同框架的 UIView,然后将您的 UITableViewCell 对象作为子视图添加到其中,从而为节标题单元格创建一个 UIView 包装器。 - mluisbrown
1
这太棒了。我一直遇到这个问题,以为是因为我的动画不够好。但它解决了大部分我的问题(即使在表视图的标题后面出现了奇怪的黑色背景),非常感谢你。 - czechboy
7
与@mluisbrown的建议不同,这个方法适用于我:返回cell.contentView; - Andrew Duncan
显示剩余8条评论

41
我会返回UITableViewCell的contentView而不是创建一个包装器,因为我考虑到在storyboard中固定约束可能会出现问题。
return cell.contentView;

1
这个答案比被接受的那个更好,因为它在视图控制器使用AutoLayout时自动适用于横向和纵向。 - Bart van Kuik
谢谢。接受的答案在iOS 8上偶尔会失效,但这个仍然有效! - Eric Chen
2
背景颜色在Xcode 7.0.1 / iOS 8/9上丢失(在Xcode 6中有效),因此我必须返回到已接受的答案。 - Eric Chen
可接受的答案建议使用 UIView 子类作为标题/页脚单元格,而不是 UITableViewCell 子类。这种方法始终有效,即使使用 AutoLayout (@BartvanKuik)。 UIView 包装器解决方案是一种快速修复的替代方法。 - mluisbrown
背景颜色丢失的原因可能是您将其应用于UITableViewCellview,请将其应用于contentView,这样它应该会正确显示,至少对我来说是一个简单的解决方法。如果您已经在故事板中将您的部分标题连接为单元格,则此答案是一个更容易的解决方法。 - RyJ

26

我遇到了同样的问题,并花费了几个小时来查找问题。结果发现我在设置单元格时调用了[textField becomeFirstResponder](此处textField是自定义tableviewcell的一部分),而[textField becomeFirstResponder]会发布keyboardWillShow通知,从而导致tableview过早地加载自己,因此引起了臭名昭著的“no index path for table cell being reused”消息。一旦我删除了那个调用,问题就消失了。


天啊!我花了两天时间试图弄清楚发生了什么。谁能想到它是设置第一响应者! - user1366265
1
我也遇到了这个问题,我把调用包装成了这样:dispatch_async(dispatch_get_main_queue(), { self.textField.becomeFirstResponder() }),然后问题就消失了。 - juanjo
这帮助我找到了问题的根源。实际上,在设置单元格时,我能够成为第一响应者,但是我还必须确保在工作流程后明确地放弃它。这样做并确保我的reloadRowsAtIndexPaths方法都在主线程上运行解决了我的问题。 - Scott D

16

除了已被接受的答案(mluisbrown)之外,我还需要为标题单元格添加一个自动调整大小的掩码,因为我的标题包含多行标签,即:

UIView *view = [[UIView alloc] initWithFrame:[cell frame]];
cell.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
[view addSubview:cell];
return view;

谢谢,这在我的iOS8上有效。在iOS7上它对我也很好用。 - Kesava
与其使用 UIView 包装器,最好将您的标题/页脚单元格制作为 UIView 子类,而不是 UITableView 子类。这样,您就不必添加自动调整大小掩码。 - mluisbrown

8

这是一个内部的UIKit bug,正如苹果公司自己的开发者论坛中所提到的。据说在新版本的Xcode中已经修复了这个问题,尽管我没有找到哪个版本可以修复此问题。


1
您必须在苹果的开发者门户网站上注册开发者账号才能访问该线程。首先登录苹果开发者门户网站,然后进入“会员中心”和“苹果开发者论坛”。在搜索框中搜索“no index path for table cell being reused”。您将找到一个帖子,其中包含我上面发布的解释。不幸的是,没有太多其他信息可用,但他们建议您提交一个错误报告。 - diegoreymendez
我也遇到了这个问题。非常烦人。 - CW0007007
1
@ChrisWagner,下面是我针对iOS 7可能的解决方案的答案链接 - mluisbrown
我遇到了这个问题,因为我的自定义表格视图单元格的textField子视图具有clearOnInsertion属性设置为TRUE。 - CW0007007
我认为这是期望的行为,下面是一个有解决方法并且有效的答案:https://dev59.com/w2cs5IYBdhLWcg3wh0f7#18769328 - czechboy
显示剩余4条评论

4
作为我之前帖子的补充(在那篇帖子中,我提到这显然是UIKit的一个bug),我能够找到适用于我的特定情况的解决方法(其中消息与表格上的一些奇怪可视化故障有关)。
显然,我的自定义单元格重写的-(void)setEditing:animated:花费了太长时间来返回。
我的先前代码如下:
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{   
    [super setEditing:editing animated:animated];
    [self someAdditionalCode];
}

我通过将其更改为以下内容来修复它:

- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{   
    [super setEditing:editing animated:animated];

    // DRM: we want to perform the actions from this block in the main thread, but
    // asynchronously to avoid excessive delays which were causing issues.
    //
    dispatch_async(dispatch_get_main_queue(), ^void()
    {
        [self someAdditionalCode];
    });
}

2

在 resignfirstresponder 后执行我的 endupdates 解决了我的问题(在自定义单元格中有一个 UITextFIeld)

-(void)textfieldEditDone
{
....

    [textField resignFirstResponder];
    [self.tableView endUpdates];

2

我曾经遇到过同样的问题,出现了错误提示信息。据我观察,这是由于从一个函数中重新加载表视图造成的,该函数是作为文本框委托协议的一部分被调用的。也就是说textFieldDidEndEditing -> [controller.tableview reload...]


1

顺便说一下,我在iOS 6上运行时也遇到了这个消息。看起来某些代码继承或导入了类似于以下内容的东西:

(NSInteger)tableView:(UITableView *)tv numberOfRowsInSection:(NSInteger)section {
    NSInteger rows = 0;
    if ([delegate respondsToSelector:@selector(numberOfItemsInSection:)]) {
        rows = [delegate numberOfItemsInSection:section];

        [tableView beginUpdates];
        [tableView endUpdates];
    }
}

当 beginUpdate: / endUpdate: 序列被移除时,问题神奇地消失了。

1
这显然是一个老问题,但希望这可以帮助任何仍然在iOS8+中遇到此问题的人,因为这仍然是出现此特定错误消息的最常见问题。
我正在使用PINRemoteImage异步下载图像到位于自定义UITableViewCell内部的UIImageView。
为了在图像加载后正确调整行的大小(使用自动布局的动态高度单元格),我调用了:
self.tableView beginUpdates;
self.tableView endUpdates;

我当时遇到了"no index path for table cell being reused"的错误信息,导致应用程序崩溃。我原以为PINRemoteImageManagerResult块在主线程上,但实际上不是这样 - 因此确保开始/结束更新在主线程上调用解决了这个问题。
dispatch_async(dispatch_get_main_queue(), ^(void){
                            [self.tableView beginUpdates];
                            [self.tableView endUpdates];
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接