带有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上有一个这样的示例实现。