What is the meaning of the "no index path for table cell being reused" message in iOS 6/7?

Since starting to compile my app with iOS 6 (and since also iOS 7) I've started seeing this message. I know that the way that UITableViews go about managing cells is different in iOS 6 but I haven't needed to modify my code for it to keep working. But I'm concerned this message may point to some potential issue that I'm not yet seeing. Can anyone shed any light?

Answers


I started to get this error showing up in the log from iOS 7 beta 5 onwards, including in the iOS 7 GM/Release build, whilst never having had it happen in my app in iOS 6 or the earlier iOS 7 betas. After a lot of experimenting I found the cause:

I was using UITableViewCell objects for my section header views and returning them in tableView:viewForHeaderInSection:. This appears to be common practice, especially since iOS 5 when it became easy to design a section header view as a prototype table view cell in a StoryBoard with Interface Builder.

When I changed my app to use just regular UIView subclasses for my section header views, the errors went away and, more importantly, my table view stopped randomly deleting section headers!

It would appear that (since iOS 7 beta 5) UITableView is internally maintaining a mapping of all the UITableViewCell objects in its view hierarchy and their respective index paths. Since a section header (or a table view header of footer) doesn't have an index path, if you use a UITableViewCell object for these views, the table view will get confused when it finds a UITableViewCell for which it doesn't have an index path, resulting in the "no index path for table cell being reused" error and, if you're unlucky, display glitches in your table view:

UPDATE: if you have access to the Apple Dev Forums, here's the thread about it (which I started): https://devforums.apple.com/message/882042#882042

As suggested in that thread, if you don't want to re-factor much, you can create a UIView wrapper around your UITableViewCell and return that as the section header view.

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

return view;

Note however that this "wrapper" UIView approach will not play well with AutoLayout and device rotation, so I suggest that you use a UIView subclass for header and footer cells, not a UITableViewCell subclass as explained in the main part of the answer.


I'd return the contentView of the UITableViewCell instead of creating a wrapper.. having constraint-jabble fixed in storybord in mind

return cell.contentView;

I had the same problem and it took me few hours to hunt down the issue. Turns out I was calling [textField becomeFirstResponder] while setting up cells (here the textField was part of a custom tableviewcell); [textField becomeFirstResponder]in turns posts keyboardWillShow notification which in turn caused the tableview to prematurely load itself thus causing the infamous "no index path for table cell being reused” message. Once I removed that call, problem disappeared.


Addition to the accepted answer (mluisbrown), I needed to add an autoresizingMask to the header cell, since mine contained a multi-line label, i.e.

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

This is an internal UIKit bug - as mentioned in Apple's own dev forums. It's supposedly fixed in newer versions of xcode, although I wasn't able to find info regarding which version fixes this.


As an addition to my previous post (in which I mentioned that this was apparently a bug with UIKit), I was able to find a workaround for my particular case (in which the message was related to some strange visualisation glitches on the table).

Apparently my custom cell's overridden -(void)setEditing:animated: was taking too long to return.

My previous code was:

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

I was able to fix it by changing it to:

- (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];
    });
}

Doing my endupdates after resignfirstresponder solved my problem (Have a UITextFIeld in my custom cell)

-(void)textfieldEditDone
{
....

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

I had the same problem with the error message appearing. As far as I can see it is caused by reloading the table view from a function called by the the textfield as part of its delegate protocol. Ie textFieldDidEndEditing -> [controller.tableview reload...]


For the record, I encountered this message too when running under iOS 6. It appears that some code either inherited or imported had something like this:

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

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

When the beginUpdate: / endUpdate: sequence was removed, the problem magically disappeared.


This is obviously an old question, but hopefully this can help anyone who still gets this issue in iOS8+ because this is still the top question that comes up for this particular error message.

I was using PINRemoteImage to async download an image to a UIImageView that was inside a custom UITableViewCell.

In order to resize the row properly (dynamic height cells using auto-layout) once the image had loaded, I called:

self.tableView beginUpdates;
self.tableView endUpdates;

I was then getting the "no index path for table cell being reused" message and the app was crashing. I had thought the PINRemoteImageManagerResult block was on the main thread, however it turns out that it wasn't - therefore ensuring that the begin/end updates was called on the main thread fixed the issue.

dispatch_async(dispatch_get_main_queue(), ^(void){
                            [self.tableView beginUpdates];
                            [self.tableView endUpdates];
});

Well, I just used the better part of a day trying to figure this out, so hopefully this alternative explanation saves somebody else some time.

I had a tableview which was giving this message sometimes, when loading. It turned out to be caused by KVO notifications for Core Data objects firing while the view was loading. (On observing a change, my controller tried called reloadData on the tableview in question. Fixed by not observing the objects until the view had finished loading (previously I started observing the object once it was assigned via the accessor)

TLDR: Check to see if you might be trying to reload your data from something other than the main thread.


Maybe this helps someone: i once had this error while refreshing a single table view cell. I meant to do something like

NSIndexPath *reloadRow = [NSIndexPath indexPathForRow:1 inSection:2];
[self._mainTableView reloadRowsAtIndexPaths:@[reloadWebViewRow]
                           withRowAnimation:UITableViewRowAnimationFade];

But accidentally, i typed

NSIndexPath *reloadRow = [NSIndexPath indexPathForItem:1 inSection:2];

Notice the difference of the two indexpaths: one is created with indexPathForItem (wrong), the other with indexPathForRow (correct). This all resulted in very strange behaviour of the tableView and the error message in the headline.


It seems like my issue was triggered when I was trying to update the UI within a portion of the code that was a callback from a web call. I resolved by forcing UI updates to happen on the main thread. I used code like this.

void runOnMainQueueWithoutDeadlocking(void (^block)(void)){
    if ([NSThread isMainThread])
    {
        block();
    }
    else
    {
        dispatch_sync(dispatch_get_main_queue(), block);
    }
}

I call it as follows inside of the success block of my background web call.

runOnMainQueueWithoutDeadlocking(^{
    [self.tableView beginUpdates];
    [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationAutomatic];
    [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:2] withRowAnimation:UITableViewRowAnimationAutomatic];
    [self.tableView endUpdates];
});

Yet another condition...

This happened when, not wanting a header, I returned nil.

Fix:

func tableView(tableView: UITableView,
               titleForHeaderInSection section: Int) -> String? {
    return ""
}

Need Your Help

Insert into C# with SQLCommand

c# .net ado.net sqlcommand

What's the best way to INSERT data into a database?

How to apply css to iframe content?

css iframe

I am unable to get CSS generated content to work for iframe elements: