开发者

ContentDidChange on UITableView with Sections

[Note, apologies for the code format has gone weird from copy and paste!] Hi,

I have a tableview which gets the data from core data and writes the data into the table using collation to index the table. The code is slightly modified of that from the Apple CoreDataBooks

It is all working fine, unless new data is entered into the core data, and then I receive an error message saying the message below. I have seen some other websites with this error but can't fine a solution, something to do with the section having 0 rows until new data is entered I believe

ConnectU[6168:207] Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. * -[NSMutableArray objectAtIndex:]: index 0 beyond bounds for empty array with userInfo (null)

The code is below

@synthesize managedObjectContext=__managedObjectContext;
@synthesize fetchedResultsController;
@synthesize delegate;
@synthesize filteredListContent;
@synthesize savedSearchTerm;
@synthesize savedScopeButtonIndex;
@synthesize searchIsActive;
@synthesize friendDataArray, sectionsArray, collation;

- (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    if (self) {

        __managedObjectContext = [(ConnectUAppDelegate*)[UIApplication sharedApplication].delegate managedObjectContext];

        self.title = @"Friends";

        UITabBarItem *item = [[UITabBarItem alloc] initWithTabBarSystemItem:UITabBarSystemItemContacts tag:0];
        self.tabBarItem = item; 
        [item release];

        NSError *error;
        if (![[self fetchedResultsController] performFetch:&error]) {
            // Update to handle the error appropriately.
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            exit(-1);  // Fail
        }

    }
    return self;
}

- (void)updateFeed:(id)sender {
    // Add some new data to the core data and save, no problems  

    [self.tableView reloadData];  
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    // Set the table view's row height
    self.tableView.rowHeight = 44.0;

    //Add the refresh button
    UIBarButtonItem* refreshButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(updateFeed:)];
    [self.navigationItem setLeftBarButtonItem:refreshButton animated:YES];
    [refreshButton release]; 


    // ******** Search Bar Set Up ******** //
    // hidden to reduce code here
    // ******** Search Bar Set Up Complete ******** //

    // Get the friends data
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:[NSEntityDescription entityForName:@"Friends" inManagedObjectContext:[self managedObjectContext]]];

    NSError *error2 = nil;
    NSArray *fetchResults2 = [[self managedObjectContext] executeFetchRequest:request error:&error2];

    if (fetchResults2) {
        friendDataArray = [NSMutableArray arrayWithArray:fetchResults2];    
    }

    [self configureSections];
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

    if (self.searchIsActive) {
        return 1;
    }

        // The number of sections is the same as the number of titles in the collation.
    return [[collation sectionTitles] count];
}


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

    // If a search is taking place, return number of items in filteredListContent
    if (self.searchIsActive) {
        return [self.filteredListContent count];
    }

        // The number of time zones in the section is the count of the array associated with the section in the sections array.
        NSArray *timeZonesInSection = [sectionsArray objectAtIndex:section];

    return [timeZonesInSection count];
}



- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    NSArray *friendsInSection = [sectionsArray objectAtIndex:section];
    if( [friendsInSection count] != 0 ) {
        return [[collation sectionTitles] objectAtIndex:section];
    }

    return @"";
}


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


- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
    return [collation sectionForSectionIndexTitleAtIndex:index];
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Friends_View";

    // Create a cell, reuse a useable cell if available
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }

    // Configure the cell
        [self configureCell:cell atIndexPath:indexPath];
    return cell;

}


- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath   {

    // If search is taking place, then get cell contents from the filteredlistcontent
    if (self.searchIsActive) {

        Friends *friend = [[self filteredListContent] objectAtIndex:[indexPath row]];
        NSString *name = [[NSString alloc] initWithFormat:@"%@ %@", [friend valueForKeyPath:@"first_name"] ,[friend valueForKeyPath:@"last_name"]];
        cell.textLabel.text = name;
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;

    // No search taking place so therefore get data from sectionsArray
    } else {

        // Get the time zone from the array associated with the section index in the sections array.
        NSArray *friendsInSection = [sectionsArray objectAtIndex:indexPath.section];

        // Configure the cell with the time zone's name.
        Friends *friend = [friendsInSection objectAtIndex:indexPath.row];


        NSString *name = [[NSString alloc] initWithFormat:@"%@ %@", [friend valueForKeyPath:@"first_name"] ,[friend valueForKeyPath:@"last_name"]];
        cell.textLabel.text = name;
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    }
}


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    Friends *friend;

    // If search is taking place, get object from filteredListContent
    if (self.searchIsActive) {
        friend = [[self filteredListContent] objectAtIndex:[indexPath row]];

    // Else get a normal friend object
    } else {
        NSArray *friendsInSection = [sectionsArray objectAtIndex:indexPath.section];
        friend = [friendsInSection objectAtIndex:indexPath.row];
    }

    // Create a friendDetail View controller and push this into the view
    FriendsDetail_ViewController *friendsDetail = [[FriendsDetail_ViewController alloc] initWithStyle:UITableViewStyleGrouped];

    NSString *name = [[NSString alloc] initWithFormat:@"%@ %@", [friend valueForKeyPath:@"first_name"] ,[friend valueForKeyPath:@"last_name"]];
    friendsDetail.title = name;
    friendsDetail.friend_data = friend;
    [friendsDetail setDelegate:self];
    [self.navigationController pushViewController:friendsDetail animated:YES];

    [friendsDetail release];

}


- (void)configureSections {

        // Get the current collation and keep a reference to it.
        self.collation = [UILocalizedIndexedCollation currentCollation];

        NSInteger index, sectionTitlesCount = [[collation sectionTitles] count];

        NSMutableArray *newSectionsArray = [[NSMutableArray alloc] initWithCapacity:sectionTitlesCount];

        // Set up the sections array: elements are mutable arrays that will contain the time zones for that section.
        for (index = 0; index < sectionTitlesCount; index++) {
                NSMutableArray *array = [[NSMutableArray alloc] init];
                [newSectionsArray addObject:array];
                [array release];
        }

        // Segregate the time zones into the appropriate arrays.
        for (Friends *friend in friendDataArray) {

                // Ask the collation which section number the time zone belongs in, based on its locale name.
                NSInteger sectionNumber = [collation sectionForObject:friend collationStringSelector:@selector(first_name)];

                // Get the array for the section.
                NSMutableArray *sectionTimeZones = [newSectionsArray objectAtIndex:sectionNumber];

                //  Add the time zone to the section.
                [sectionTimeZones addObject:friend];
        }

        // Now that all the data's in place, each section array needs to be sorted.
        for (index = 0; index < sectionTitlesCount; index++) {

                NSMutableArray *timeZonesArrayForSection = [newSectionsArray objectAtIndex:index];

                // If the table view or its contents were editable, you would make a mutable copy here.
                NSArray *sortedTimeZonesArrayForSection = [collation sortedArrayFromArray:timeZonesArrayForSection collationStringSelector:@selector(first_name)];

                // Replace the existing array with the sorted array.
                [newSectionsArray replaceObjectAtIndex:index withObject:sortedTimeZonesArrayForSection];
        }

        self.sectionsArray = newSectionsArray;
        [newSectionsArray release]; 
}


- (NSFetchedResultsController *)fetchedResultsController {

    // Set up the fetched results controller if needed.
    if (fetchedResultsController == nil) {
        // Create the fetch request for the entity.
        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        // Edit the entity name as appropriate.
        NSEntityDescription *entity = [NSEntityDescription entityForName:@"Friends" inManagedObjectContext:[self managedObjectContext]];
        [fetchRequest setEntity:entity];

        // Edit the sort key as appropriate.
        NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"first_name" ascending:YES selector:nil];
        NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];

        // Recover query


        [fetchRequest setSortDescriptors:sortDescriptors];

        // Edit the section name key path and cache name if appropriate.
        // nil for section name key path means "no sections".
        NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[self managedObjectContext] sectionNameKeyPath:nil cacheName:@"Root"];
        aFetchedResultsController.delegate = self;
        self.fetchedResultsController = aFetchedResultsController;

        [aFetchedResultsController release];
        [fetchRequest release];
        [sortDescriptor release];
        [sortDescriptors release];

    }

        return fetchedResultsController;
}




- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
        // The fetch controller is about to start sending change notifications, so prepare the table view for updates.
        [self.tableView beginUpdates];
}


- (void)controll开发者_开发知识库er:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {

        UITableView *tableView = self.tableView;

        switch(type) {

                case NSFetchedResultsChangeInsert:
                        [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
                        break;

                case NSFetchedResultsChangeDelete:
                        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
                        break;

                case NSFetchedResultsChangeUpdate:
                        //[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
                        break;

                case NSFetchedResultsChangeMove:
                        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
                        break;
        }
}


- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {

        switch(type) {

                case NSFetchedResultsChangeInsert:
                        [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
                        break;

                case NSFetchedResultsChangeDelete:
                        [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
                        break;
        }
}


- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
        // The fetch controller has sent all current change notifications, so tell the table view to process all updates.
        [self.tableView endUpdates];
}


You shouldn't be mixing a UILocalizedIndexedCollation and a NSFetchedResultsController. Use one or the other.

The collation takes its indexes from the tableview as it was when it was first created. When the FRC adds a new row, the index of the collation becomes invalid producing the error.

If you are using a FRC, you have no need for a collation because the FRC manages sections for you as well and it keeps them properly updated for changes in data.


Yer I noticed what I was doing wrong. I removed the NRC functions and used the following code

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { [self configureSections]; [self.tableView reloadData]; }

Within the configureSections, I grab the data from the fetched controller into the friendDataArray and then reload the table. Works :)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜