带有UICollectionView的NSFetchResultController与索引/单元格有关的更新和删除操作

第一次,我使用UICollectionView,我有一些困难。 特别是更新和删除单元格(将重点放在这里删除,希望NSFetchResultController ),从NSFetchResultController数据。 我在界面生成器中创build了一个自定义单元格,作为这样一个故事板的一部分:

在这里输入图像说明

我有一个自定义的UICollectionViewCell子类具有以下属性:

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

问题如下:

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

任何想法如何解决这些问题将是伟大的!

更新

如下所示,我的deleteNote操作中的这个日志对于indexPath行总是返回0。 不知道这是否是原因。

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

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

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

每次都configureCell:atIndexPath: ,以便在单元格被修改或重用时,多个操作被附加到button上。 然后deleteNote:一次触摸事件将被多次调用。

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

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

您不能像访问表格视图那样将抓取的结果控制器链接到集合视图。 一个获取的结果控制器是专门devise用于在核心数据和表视图之间进行调解的,所以委托方法与表视图的工作方式相配合。

主要的区别是表视图有一个beginUpdates / endUpdates方法对,你可以包装所有的更新。集合视图没有这个,你必须自己构build所有需要的更新调用,然后当获取的结果控制器已经完成更新,在performBatchUpdates:completion: call中执行它们。

在GitHub上有一个这样的示例实现。