ios - NSFetchedResultsController indexPathForObject 不计算部分

标签 ios uitableview nsfetchedresultscontroller

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

我获取索引和更新下载进度的代码是这样的:

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 和委托(delegate)。

- (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];
}

最佳答案

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

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

并且 configureCell: 方法应该从 cellForRowAtIndexPath 方法中调用,而不是从获取的结果 Controller 委托(delegate)方法中调用。一般模式是在 controllerDidChangeObject: 中调用 reloadRowsAtIndexPaths:。否则,您可能会遇到一些单元重用问题。

关于代码应该是什么样子的想法:

- (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];
}

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

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

最后,配置单元格(假设您从 tableView:cellForRowAtIndexPath: 调用 configureCell:atIndexPath):

- (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
}

关于ios - NSFetchedResultsController indexPathForObject 不计算部分,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27556691/

相关文章:

ios - 2 iAds 在模拟器上

ios - 使用 2 个不同的对象创建 UITableView

iOS 7 添加 UIView 到 UITableView

ios - NSFetchedResultsController 使加载 View Controller 非常慢

swift - 未从相关实体 swift 调用部分更新事件

ios - 为什么 ProjectName-Prefix.pch 没有在 Xcode 6 中自动创建?

ios - 保存图像以进行解析

iphone - 像 iPod 应用程序一样使用 UITableView 气泡/标注

iphone - 选择 UITableView 的多行

ios - 如何在 swift 中使用 fetchedresultscontroller 重新排序 tableview 行?