同时移动和更新UITableViewCell和NSFetchedResultsController

时间:2013-01-16 08:47:10

标签: ios uitableview ios6 nsfetchedresultscontroller

我有一个简单的表视图,包含1个部分和2个行。我使用NSFetchedResultsController来保持表与CoreData同步。我对CD中的一行进行了更改,触发了一个要更新和移动的表视图单元格。问题是,在cellForRowAtIndexPath:期间调用NSFetchedResultsChangeUpdate时,会返回错误的单元格(这有意义b / c尚未移动单元格)。因此,使用新更新的数据更新了错误的单元格。之后处理NSFetchedResultsChangeMove消息,以便单元格交换位置(单元格的内容都不会更新,因为它只是一个移动调用)。结果是两个单元都反映了来自新更新的CD实体的数据。重新加载表可以解决问题。我正在运行iOS 6。 换句话说,如果索引0处的单元格表示实体A,索引1表示实体B,并且我将实体A更新为A',使得2个单元格反向排序,结果是我看到0:A'1:A当我期待0:B,1:A'。

- (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:
//the wrong cell is updated here
            [self configureCell:(SyncCell*)[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;

        case NSFetchedResultsChangeMove:
            [tableView moveRowAtIndexPath:indexPath toIndexPath:newIndexPath];
//this code produces errors too
            //[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            //[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}

4 个答案:

答案 0 :(得分:6)

解决方案是:

[self configureCell:(SyncCell*)[tableView cellForRowAtIndexPath:indexPath] atIndexPath:newIndexPath ? newIndexPath : indexPath];

在更新期间提供新索引路径。然后使用delete和insert而不是move。我还是想知道是否有其他人有任何意见。

答案 1 :(得分:4)

我建议你看一下这个blogpost

  

修复错误很容易。只需依赖我上面描述的UITableView的行为,并使用reloadRowsAtIndexPaths:withRowAnimation:方法替换对configureCell:atIndexPath:的调用,这将自动做正确的事情:

case NSFetchedResultsChangeUpdate:
   [tableView reloadRowsAtIndexPaths:@[indexPath]  withRowAnimation:UITableViewRowAnimationAutomatic];
   break;

答案 2 :(得分:3)

调用configureCell时,根据传入的对象查找indexPath。此时indexPath和newIndexPath都不可靠。例如:

case NSFetchedResultsChangeUpdate:
    myPath = [controller indexPathForObject:anObject];
    if (myPath) {
        [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:myPath];
    }

    break;

答案 3 :(得分:1)

唯一对我有用的解决方案:Thomas Worrall's Blog: Reordering rows in a UITableView with Core Data

首先,必须在NSManagedObject中创建一个属性来挂起对象的最后一个顺序。

@interface MyEntity : NSManagedObject

@property (nonatomic, retain) NSNumber * lastOrder;

@end

然后,在ViewController.m中声明一个属性:

@interface ViewController ()

@property (nonatomic) BOOL isReordering;

@end

在方法 tableView:moveRowAtIndexPath:toIndexPath:中,管理临时数组中的更改:

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
    _isReordering = YES;

    NSMutableArray *arrayNewOrder = [[_fetchedResultsController fetchedObjects] mutableCopy];
    MyEntity *myManagedObject = [arrayNewOrder objectAtIndex:fromIndexPath.row];
    [arrayNewOrder removeObjectAtIndex:fromIndexPath.row];
    [arrayNewOrder insertObject:myManagedObject atIndex:toIndexPath.row];

    for (int i=0; i < [arrayNewOrder count]; i++)
    {
        myManagedObject = [arrayNewOrder objectAtIndex:i];
        myManagedObject.lastOrder = [NSNumber numberWithInt:i];
    }

    _isReordering = NO;
    NSError *error;
    BOOL success = [self.fetchController performFetch:&error];
    if (!success)
    {
        // Handle error
    }

    success = [[self managedObjectContext] save:&error];
    if (!success)
    {
        // Handle error
    }
}

Thomas解释了执行获取和保存上下文:

  

我不完全确定为什么需要首先执行提取,但是   当我这样做时,它修复了大量的疯狂错误!

最后一个技巧是处理NSFetchedResultsController委托方法 controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:,特别是在 NSFetchedResultsChangeMove:

case NSFetchedResultsChangeMove:
{
    if (!_isReordering)
    {
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                         withRowAnimation:UITableViewRowAnimationFade];
        [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
                         withRowAnimation:UITableViewRowAnimationFade];
    }

    break;
}

它对我有用!我希望它可以帮到你!