NSFetchedResultsController indexPathForObject不计算节

时间:2014-12-18 22:16:52

标签: ios uitableview nsfetchedresultscontroller

我正在更新单元格上的下载视图/按钮,当我去更新我的单元格时,我没有得到正确的部分。

获取索引并更新下载进度的代码是:

Object *obj = (Object *)notification.object;
NSIndexPath *index = [self.fetchedResultsController indexPathForObject:obj];
MyTableViewCell *cell = (MyTableViewCell *)[self.tableView cellForRowAtIndexPath:index];
DownloadProgressButtonView *buttonView = (DownloadProgressButtonView *)cell.accessoryView;
NSNumber *progressLong = [notification.userInfo objectForKey:@"progress"];
float progress = [progressLong floatValue];
NSNumber *totalBytesLong = [notification.userInfo objectForKey:@"totalBytes"];
float totalBytes = [totalBytesLong floatValue];
buttonView.progress = progress *.01;
float totalDownloadEstimate = totalBytes / 1.0e6;
float megaBytesDownloaded = (progress *.01) * totalDownloadEstimate;
cell.bottomLabel.text = [NSString stringWithFormat:@"%.1f MB of %.1f MB", megaBytesDownloaded, totalDownloadEstimate];

如果我有两个对象,每个对象在不同的​​部分,则它们具有相同的行(0)。当我去更新我的单元格时,它会更新第1部分中的单元格而不是第0部分。如何修复此问题?

我可以放任何其他需要的代码。如果我只是在我的NSFetchedResultsController中禁用部分,它可以完美地工作。

我的NSFetchedResultsController和委托。

- (NSFetchedResultsController *)fetchedResultsController
{

    if (_fetchedResultsController != nil) {
        return _fetchedResultsController;
    }

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Object" inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:entity];

    NSSortDescriptor *nameString = [[NSSortDescriptor alloc] initWithKey:self.sectionSortDescriptor ascending:NO];
    NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:self.sortDescriptor ascending:YES];
    [fetchRequest setSortDescriptors:[NSArray arrayWithObjects:nameString,descriptor, nil]];
    NSString *downloadStartedString = @"Preparing to download";
    NSString *downloadingString = @"Downloading";
    NSString *downloadPausedString = @"Download paused";
    fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(downloaded == YES) OR (downloadStatus like[cd] %@) OR (downloadStatus like[cd] %@) OR (downloadStatus like[cd]%@)",downloadPausedString, downloadStartedString,downloadingString];
    [fetchRequest setFetchBatchSize:20];
    _fetchedResultsController =
    [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                        managedObjectContext:self.managedObjectContext sectionNameKeyPath:self.sectionNameString
                                                   cacheName:nil];
    _fetchedResultsController.delegate = self;
    self.fetchedResultsController = _fetchedResultsController;

    return _fetchedResultsController;
}

/*
 NSFetchedResultsController delegate methods to respond to additions, removals and so on.
 */
- (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:@[newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
            break;

        case NSFetchedResultsChangeDelete:
            [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
            break;

        case NSFetchedResultsChangeUpdate:
            [self configureCell:(StudioTableViewCell *)[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;

        case NSFetchedResultsChangeMove:
            [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
            [tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
            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:UITableViewRowAnimationAutomatic];
            break;
        case NSFetchedResultsChangeDelete:
            [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationAutomatic];
            break;
        case NSFetchedResultsChangeMove:
            NSLog(@"A table item was moved");
            break;
        case NSFetchedResultsChangeUpdate:
            NSLog(@"A table item was updated");
            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];
}

最后,当下载状态发生变化时,我会更新对象并发送通知以更新具有新状态的单元格:

- (void)updateCell:(NSNotification *)notification
{
    Object *obj = (Object *)notification.object;
    NSIndexPath *index = [self.fetchedResultsController indexPathForObject:obj];
    [self.tableView reloadRowsAtIndexPaths:@[index] withRowAnimation:UITableViewRowAnimationFade];
}

1 个答案:

答案 0 :(得分:1)

以这种方式更新单元格并不可靠。以这种方式更新的单元格迟早会被重用。根据数据源提供的数据,tableView:cellForRowAtIndexPath:将重新配置单元格的子视图。

您应该对Object本身进行更改(而不是在通知&#39; userInfo中传递它们)并保存托管对象上下文。然后将触发NSFetchedResultsControllerDelegate回调,允许您重新加载相应的行。然后,您应该在MyTableViewCell中设置configureCell:atIndexPath的所有属性。

configureCell:方法应该从cellForRowAtIndexPath方法调用,而不是从获取的结果控制器委托方法调用。一般模式是在reloadRowsAtIndexPaths:中调用controllerDidChangeObject:。否则,您可能会遇到一些细胞重用问题。

关于代码应该如何的想法:

- (void)updateCell:(NSNotification *)notification
{
    //depending on your Core Data contexts setup,
    // you may need embed the code below in performBlock: on object's context,
    // I omitted it for clarity
    Object *obj = (Object *)notification.object;
    //save changes to the object, for example:
    NSNumber *progressLong = [notification.userInfo objectForKey:@"progress"];
    obj.progress = progressLong; 
    //set all the properties you will need in configureCell:, then save context
    [obj.magagedObjectContext save:&someError];
}

然后获取的结果控制器将调用controllerDidChangeObject:,在这种方法中你应该重新加载行:

    case NSFetchedResultsChangeUpdate:
        [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
        break;

最后,配置单元格(让我们假设您从configureCell:atIndexPath拨打tableView:cellForRowAtIndexPath:):

- (void)configureCell:(MyTableViewCell*)cell atIndexPath:(NSIndexPath*)indexPath {
    Object *object = [self.fetchedResultsController objectAtIndexPath:indexPath];

    DownloadProgressButtonView *buttonView = (DownloadProgressButtonView*) cell.accessoryView;
    buttonView.progress = object.progress.floatValue *.01;
    //and so on
}