NSFetchedResultsController 与 CollectionView 问题与索引/单元格更新和删除操作

Posted

技术标签:

【中文标题】NSFetchedResultsController 与 CollectionView 问题与索引/单元格更新和删除操作【英文标题】:NSFetchResultController with UICollectionView issue with indexes/cells on update and delete action 【发布时间】:2013-08-10 06:57:10 【问题描述】:

我第一次使用 UICollectionView,但遇到了一些困难。尤其是更新和删除单元格(这里将重点关注删除,希望是原因),来自NSFetchResultController 的数据。我在界面生成器中创建了一个自定义单元格,作为故事板的一部分,如下所示:

我有一个自定义的 UICollectionViewCellsubclass 具有以下属性:

@property (strong, nonatomic) IBOutlet UIButton *deleteButton;
@property (strong, nonatomic) IBOutlet UITextField *textField;
@property (strong, nonatomic) IBOutlet UIView *containerView;
@property (strong, nonatomic) IBOutlet UIView *textFieldContainer;

在 IB 中,我将单元类设置为自定义类,将元素连接到自定义类的属性,并将标识符设置为 Cell

在我的集合视图视图控制器中,我设置了集合视图和 fetchResultController 以及相关方法,如下所示:

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView

    return [[self.fetchedResultsController sections] count];


-  (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section

    id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];
    return [sectionInfo numberOfObjects];


- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath

    NoteCollectionViewCell* cell = (NoteCollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
    [self configureCell:cell atIndexPath:indexPath];
    return cell;


- (void)configureCell:(NoteCollectionViewCell *)cell atIndexPath:(NSIndexPath *)indexPath

    cell.deleteButton.tag = indexPath.row;
    [cell.deleteButton addTarget:self action:@selector(deleteNote:)  forControlEvents:UIControlEventTouchUpInside];

    [...]

    // I'm having some weird problem with this, se description below. 
    Note *note = [self.fetchedResultsController objectAtIndexPath:indexPath];
    cell.textField.tag = indexPath.row;
    cell.textField.delegate = self;
    cell.textField.text = note.name;
    [...]

#pragma mark - Fetched results controller

- (NSFetchedResultsController *)fetchedResultsController

    [Default FRC method]


- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
   atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
  newIndexPath:(NSIndexPath *)newIndexPath

    UICollectionView *collectionView = self.collectionView;

    switch(type) 
        case NSFetchedResultsChangeInsert:
            [collectionView insertItemsAtIndexPaths:@[newIndexPath]];
            break;

        case NSFetchedResultsChangeDelete:
            [collectionView deleteItemsAtIndexPaths:@[indexPath]];
            break;
        case NSFetchedResultsChangeUpdate:
            [self configureCell:(NoteCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath] atIndexPath:indexPath];
            break;
    

我的删除操作如下所示:

- (void)deleteNote:(UIButton *)sender

    Note *note = [self.fetchedResultsController objectAtIndexPath:[NSIndexPath  indexPathForItem:sender.tag inSection:0]];
    [[AppDelegate sharedAppDelegate].managedObjectContext deleteObject:note];
    [[AppDelegate sharedAppDelegate] saveContext];

我的更新操作(UITextField 委托方法)如下所示:

- (void)textFieldDidEndEditing:(UITextField *)textField

    Note *note = [self.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForItem:textField.tag inSection:0]];
    note.name = textField.text;
    [[AppDelegate sharedAppDelegate] saveContext];

问题如下:

删除按钮并不总是删除正确的单元格/对象。有时对象/单元格根本不会被删除。 有时当我删除对象时应用程序崩溃(SIGARBT 错误)。 有时文本显示在错误的文本字段中。 有时当我删除带有一些文本的第 0 行,然后添加按添加时,会添加一个新单元格,其文本值与前一个(已删除)单元格相同。

任何关于如何解决这些问题的想法都会很棒!

更新

作为下面的评论,我的deleteNote 操作中的此日志始终为 indexPath 行返回 0。不知道会不会是这个原因。

NSIndexPath *indexPath = [NSIndexPath indexPathForItem:sender.tag inSection:0];
NSLog(@"indexPath: %@. Row: %d", indexPath, indexPath.row);

【问题讨论】:

当您记录 indexPath 时,是否显示您尝试删除的单元格? 我的delete action 中的以下日志由于某种原因总是为该行返回 0:NSIndexPath *indexPath = [NSIndexPath indexPathForItem:sender.tag inSection:0]; NSLog(@"indexPath: %@. Row: %d", indexPath, indexPath.row); 我会检查 NSFetchedResultsController。向后工作,因为我怀疑是 indexPath 造成了问题。 谢谢,但我从 collectionView 委托方法中获得了索引路径(!?!)。 【参考方案1】:

您不能以与表格视图完全相同的方式将获取的结果控制器链接到集合视图。获取结果控制器专门设计用于在核心数据和表视图之间进行调解,因此委托方法与表视图的工作方式相关联。

主要区别在于表视图有一个 beginUpdates/endUpdates 方法对,您可以将所有更新包装在其中。集合视图没有这个,您必须构建所有required update 调用自己,然后当获取的结果控制器完成更新时,在performBatchUpdates:completion: 调用中执行它们。

这个on GitHub 有一个示例实现。

【讨论】:

我尝试过使用它(现在两次),但我遇到了同样的问题。我用的是ios7,不知道有没有什么区别... 您问题中的代码没有任何迹象。您需要更新代码以使用该技术,然后发布您遇到的问题。 另外,如果我改用- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller [self.collectionView reloadData]; ,我也会遇到同样的问题。【参考方案2】:

我不知道这是否能解释你的问题,但你打电话

[cell.deleteButton addTarget:self action:@selector(deleteNote:)  forControlEvents:UIControlEventTouchUpInside];

每次configureCell:atIndexPath: 中,以便将多个动作附加到 如果单元格被修改或重用,则按钮。然后将调用deleteNote: 一键式事件不止一次。

作为一种解决方法,您可以检查 cell.deleteButton.tag 以查看是否已将操作附加到按钮,或者更好:

NoteCollectionViewCell 类中添加操作 创建单元格时,该类中的本地操作, 使用委托将操作转发到集合视图控制器。

【讨论】:

似乎这就是问题所在。我所做的是为每个单元格添加一个Note(模型)属性,并检查它是否在configureCell:atIndexPath: 中为nil,如果它为nil,则仅设置选择器/操作。

以上是关于NSFetchedResultsController 与 CollectionView 问题与索引/单元格更新和删除操作的主要内容,如果未能解决你的问题,请参考以下文章

在 Core Data 应用程序中调用 performFetch 后,是不是需要手动更新表视图?