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

14

58

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?

Anadem answered 7/10, 2012 at 19:38 Comment(0)
P
132

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.

Prophesy answered 12/9, 2013 at 16:10 Comment(13)
Awesome! I was experiencing the header disappearing as well. This is going to save me a lot of time over the next few days. Thank you! So were you able to design your headers in the Storyboard still? Do you just change the class to a UIView within the Storyboard settings??Forewing
@ChrisWagner no, I just created a separate standalone XIB file for the header cell and then load it like this: NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:@"SectionHeader" owner:self options:nil]; SectionHeaderView *sectionHeaderView = nib[0]; You can't create a standalone view in a StoryBoard which isn't a view controller as far as I know.Prophesy
@ChrisWagner an alternative which also seems to work is to just create a UIView with the same frame and then add your UITableViewCell object as a subview, so creating a UIView wrapper for the section header cell.Prophesy
There's another good idea, I'll try that for simplicity sake. I'd rather the design be in the storyboard with everything else.Forewing
This is amazing. I was having this problem and thought it was because my hacky animations. It solved most of my problems (even with weird black background appearance behind the headers) with table views. Thank you very much.Laktasic
Wrapping the UITableViewCell inside UIView (and still using the Storyboard prototype from [tableView dequeueReusableCellWithIdentifier:...] solved the issue with disappearing header view in iOS 7.Soluk
Excellent! Works like a charm. Initially I was returning the contentView or the tableviewcell iteself, but it doesn't work well.Yim
This didn't automatically work for me in both landscape and portrait when the view controller uses AutoLayout and the tableview is defined in Interface Builder.Switzerland
As an alternative to @Prophesy suggestion, this worked for me: return cell.contentView;Mesomorph
@AndrewDuncan The problem with simply returning the contentView is that all the targets/delegates on the cell will stop working. So only do this if you have static contentVicarious
I was also doing same, i.e returning UITableViewCell (nib) object from, viewForHeaderInSection, and getting this message logged 'no index path for table cell being reused header'. Also i was adding and removing cell dynamically inside section on tap of headerView, that was also not working smoothly. After returning view object it start working very flawlessly. Thanks!Dniren
@Prophesy is this answer still relevant in iOS 10, XCode 8.3?..I'm having a similar issue and wanted to ask if you can take a look at it? #43062219Dabchick
@Dabchick haven't tried this recently, but if you're using a UITableViewCell for a header or footer view then that's your problem. Try using just a regular UIView subclass.Prophesy
W
41

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

return cell.contentView;
Wheelsman answered 10/12, 2014 at 17:12 Comment(5)
This is a better answer than the one that's accepted, because it automatically works in both landscape and portrait when the view controller uses AutoLayout.Switzerland
Thank you. The accepted answer fails to work in iOS 8 on occasions but this one still works!Judoka
Background color is lost on Xcode 7.0.1 / iOS 8/9 (used to work for Xcode 6), so I have to go back to the accepted answer.Judoka
The accepted answer suggests using a UIView subclass as a header / footer cell not a UITableViewCell subclass. This will always work, even with AutoLayout (@BartvanKuik). The UIView wrapper solution is a quick fix alternative.Prophesy
Background color is only lost because you probably applied it to the UITableViewCell view, apply it to the contentView and it should appear correctly, at least that was the easy fix for me. This answer is a much easier fix if you've already wired your section headers up as cells in a storyboard.Prickett
C
26

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.

Cypro answered 31/12, 2013 at 2:45 Comment(3)
OMG!!! I spent two days trying to figure out what's going on. Who would have thought it is setting first responder!Odessaodetta
I had this problem too, I did wrap the call like this: dispatch_async(dispatch_get_main_queue(), { self.textField.becomeFirstResponder() }) and the problem disappearedPattypatulous
This helped lead me to the source of my problem. I was actually able to becomeFirstResponder while setting up the cell, however, I had to also make sure it was explicitly resigned later in the workflow. That and ensuring that my reloadRowsAtIndexPaths methods were all running on the main thread resolve my issues.Abib
U
16

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;
Unfeeling answered 31/3, 2014 at 1:47 Comment(2)
Thanks this worked for me in ios8. in ios 7 it worked fine for meIllconsidered
Rather than using a UIView wrapper, it would be better to make your header / footer cells UIView subclasses, not UITableView subclasses. That way you wouldn't have to add an autoresizing mask.Prophesy
T
8

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.

Timmi answered 7/10, 2012 at 21:39 Comment(9)
Ah thanks. I did do a search for the exact phrase in Google but nothing came up. Perhaps it hasn't been indexed yet. Don't suppose you happen to have a direct link do you?Anadem
You have to be registered as a developer in Apple's Dev portal to access the thread. First log in to Apple's Dev Portal. Then go to "Member Center", and "Apple Developer Forums". In the search box look for "no index path for table cell being reused". You will find one thread with the explanation I posted above. Unfortunately not much more info is available, but they do recommend that you post a bug report.Timmi
Of course. It didn't occur to me that Google wouldn't have access to the forums. Thanks!Anadem
I am experiencing this too. Very annoying.Hydrocellulose
@ChrisWagner, see my answer below for a possible solution for iOS 7.Prophesy
@Hydrocellulose maybe my answer below can help you. Apologies for the duplicate comment, but you can't @-reply to more than one person at a time.Prophesy
I had this problem because a textField subview of my custom tableview cell had the property clearOnInsertion set to TRUE.Hydrocellulose
I think this is desired behaviour, the answer that has a workaround and works is below: https://mcmap.net/q/137855/-what-is-the-meaning-of-the-quot-no-index-path-for-table-cell-being-reused-quot-message-in-ios-6-7Laktasic
XCode 5.1.1 still hereGower
T
4

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];
    });
}
Timmi answered 7/10, 2012 at 23:32 Comment(0)
T
2

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...]

Tormentor answered 1/11, 2013 at 18:44 Comment(0)
S
2

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

-(void)textfieldEditDone
{
....

    [textField resignFirstResponder];
    [self.tableView endUpdates];
Semifinal answered 19/4, 2014 at 8:25 Comment(0)
C
1

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.

Cherrylchersonese answered 15/4, 2013 at 19:32 Comment(0)
S
1

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];
});
Stercoricolous answered 2/9, 2015 at 0:4 Comment(0)
K
0

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.

Keyser answered 9/4, 2014 at 11:16 Comment(0)
J
0

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.

Jasperjaspers answered 17/4, 2014 at 7:13 Comment(0)
A
0

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];
});
Amara answered 16/1, 2015 at 4:52 Comment(1)
well after testing my code a bit more i see that the issue has came back.Amara
R
0

Yet another condition...

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

Fix:

func tableView(tableView: UITableView,
               titleForHeaderInSection section: Int) -> String? {
    return ""
}
Recant answered 20/10, 2015 at 15:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.