views:

708

answers:

5

I am populating a UITableViewController with an NSFetchedResultsController with results creating sections that populate section headers and a section index. I am using the following method to populate the section index:

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
    return [fetchedResultsController_ sectionIndexTitles];
}

and now I've run into a problem. When I add a new element to the NSManagedObjectContext associated with the NSFetchedResultsController, the new element is saved and appropriately displayed as a cell in the UITableView ... except for one thing. If the new element creates a new SECTION, the new section index does not show up in the right hand margin unless I pop the UINavigationController's stack and reload the UITableViewController.

I have conformed to the NSFetchedResultsControllerDelegate's interface and manually invoke

[self.tableView reloadSectionIndexTitles];

at the end of both these delegate methods:

controller:didChangeSection... 
controller:didChangeObject...

and while I can debug and trace the execution into the methods and see the reload call invoked, the UITableView's section index never reflects the section changes.

Again, the data shows up - new sections are physically visible (or removed) in the UITableView but the section indexes are not updated.

Am I missing something?

A: 

Try putting it at the end of -controllerDidChangeContent:, somewhere after [self.tableView endUpdates].

Alex Reynolds
Thanks for the suggestion. Unfortunately that didn't work for me. I think it might be a bug but not sure how to check or confirm. Please see the comment I added to the original question. Thanks for the suggestion.
Luther Baker
+2  A: 

Looks like this is a bug we're all having. See http://iphonedevelopment.blogspot.com/2009/11/i-know-youre-tired-of-hearing-about.html for what looks to me like a fairly nasty too-many-lines-of-code solution. I went with this:

- (void)viewWillAppear:(BOOL)animated; {
  // This is a dumb hack required by this bug: http://iphonedevelopment.blogspot.com/2009/11/i-know-youre-tired-of-hearing-about.html
  [self.tableView reloadData];
}

It may be inefficient but unless you have reams and reams of data it probably won't do any harm. And it's only 1 line of code. So, when apple fixes their bug, you can easily take it out.

sbwoodside
This actually works. I'm going to do without the section index for the time being but thanks for the research. +1.
Luther Baker
A: 

Another thing I do (that works for me, can't guarantee it will work for you) is perform a selector after a very short delay, e.g.:

[self performSelector:(@selector(refreshSectionIndex)) withObject:nil afterDelay:0.2];

// ...

- (void) refreshSectionIndex {
    [self.tableView reloadSectionIndexTitles];
}    

Core Data and NSFetchedResultsController in particular seem buggy as hell, where delegate table view updates get out of sync with the fetched data, causing the application to crash. I really hope Apple is taking steps to fix the bugs in these frameworks in the 4.0 SDK. It's pretty frustrating.

Alex Reynolds
I tried this after saving a new element. The new element shows up. The element's new section shows up - but unfortunately the new section index title is missing. Thanks again for the suggestions.
Luther Baker
A: 

Question already 2 months old, but I ran into the same problem today. It seems like -reloadSectionIndexTitles is not working at all, so I tried a couple of potential hacks which of the following works for me:

@implementation UITableView (JKAdditions)

- (UIView *)indexView {
    Class indexClass = NSClassFromString(@"UITableViewIndex");
    for(UIView *subview in self.subviews){
        if([subview isKindOfClass:indexClass]) return subview;
    }
    return nil;
}

- (void)reloadSectionIndexTitles {
    UIView *indexView = [self indexView];
    [indexView performSelector:@selector(setTitles:) withObject:[self.dataSource sectionIndexTitlesForTableView:self]];
    [indexView setNeedsDisplay];
}

@end

I really have no idea if Apple would reject your App because of this hack, but it seems like the only option for me. Reloading the whole tableView is simply not what I want since I then have to deal with all kinds of animation problems.

I hope this helps anyone having the same problems!

JoostK
A: 

To combine the accepted answer with Alex Reynolds's answer with the delay, just call reloadData with a delay that corresponds to the animation duration, so 0.4 or 0.3 seconds.

In my case, I stick the delayed method call into controller:didChangeSection:atIndex:forChangeType: (it's a Core Data app).

The result, when a section is added or deleted, is the standard animation of the cell, followed by the index being updated when the data is reloaded.

It's ugly and makes me cringe, but I am okay with the result. I also submitted a bug to Apple, #8589547.

Christoph