带有节的UITableView上的ContentDidChange

时间:2011-03-25 22:52:58

标签: uitableview core-data

[注意,代码格式的道歉因复制和粘贴而变得奇怪!] 您好,

我有一个tableview,它从核心数据中获取数据,并使用排序规则将数据写入表中以索引表。代码略微修改了Apple CoreDataBooks

的代码

一切正常,除非将新数据输入核心数据,然后我收到一条错误消息,说明下面的消息。我已经看到其他一些网站出现此错误,但无法解决问题,与有0行的部分有关,直到输入新数据我相信

  

ConnectU [6168:207]严重的应用程序错误。在调用-controllerDidChangeContent:期间,从NSFetchedResultsController的委托中捕获到异常。 * - [NSMutableArray objectAtIndex:]:索引0超出带有userInfo的空数组的边界(null)

代码在

之下
@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)controller:(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];
}

2 个答案:

答案 0 :(得分:0)

您不应该混合使用UILocalizedIndexedCollat​​ion和NSFetchedResultsController。使用其中一种。

排序规则从表视图中获取索引,就像它第一次创建时一样。当FRC添加新行时,排序规则的索引将变为无效,从而产生错误。

如果您正在使用FRC,则无需进行整理,因为FRC也会为您管理部分,并使数据更改能够正确更新。

答案 1 :(得分:0)

是的,我注意到我做错了什么。我删除了NRC函数并使用了以下代码

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

在configureSections中,我从获取的控制器中获取数据到friendDataArray,然后重新加载表。作品:))