6

我有一个包含 1 节和 2 行的简单表格视图。我正在使用 aNSFetchedResultsController使表与 CoreData 保持同步。我对 CD 中的一行进行了更改,这会触发要更新和移动的表格视图单元格。问题是,当cellForRowAtIndexPath:在 期间被调用时NSFetchedResultsChangeUpdate,会返回错误的单元格(这很有意义,因为单元格尚未移动)。因此,使用新更新的数据更新了错误的单元格。在那之后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

4 回答 4

7

一个解决方案是:

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

使用更新期间提供的新索引路径。然后使用删除和插入而不是移动。我仍然想知道其他人是否有任何意见。

于 2013-01-16T09:32:05.557 回答
5

我建议你看看这篇博文

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

case NSFetchedResultsChangeUpdate:
   [tableView reloadRowsAtIndexPaths:@[indexPath]  withRowAnimation:UITableViewRowAnimationAutomatic];
   break;
于 2013-08-01T18:56:44.063 回答
3

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

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

    break;
于 2015-08-12T19:55:56.240 回答
2

唯一对我有用的解决方案: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;
}

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

于 2014-09-02T22:07:35.110 回答