#iphone #objective-c #core-data #nspredicate #nsfetchedresultscontroller
#iPhone #objective-c #core-данные #nspredicate #nsfetchedresultscontroller
Вопрос:
Хорошо, я становлюсь лучше в этом материале Core Data, но у меня есть пути. Вот как я заполняю свой fetchedResultsController при загрузке моего представления:
- (NSFetchedResultsController *)fetchedResultsController
{
if (__fetchedResultsController != nil)
{
return __fetchedResultsController;
}
/*
Set up the fetched results controller.
*/
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Visit" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"Queue"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSPredicate *predicate =[NSPredicate predicateWithFormat:@"(isActive == YES)"];
[fetchedResultsController.fetchRequest setPredicate:predicate];
NSError *error = nil;
if (![self.fetchedResultsController performFetch:amp;error])
{
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
return __fetchedResultsController;
}
Это отлично работает. У меня есть fetchedResultsController, подключенный к моему TableView, и у меня есть 5 управляемых объектов, которые отображаются. Проблема, с которой я сталкиваюсь, заключается в том, что мне нужно внести изменения в один из управляемых объектов.
Как вы можете видеть в предикате, я указываю, что мне нужны только управляемые объекты, которые имеют isActive == YES
. Мне нужно изменить статус isActive управляемого объекта на NO, а затем удалить его из fetchedResultsController и, в конечном счете, из TableView.
Вот как я пытаюсь это сделать:
-(void) seatedButton{
Visit * vis = [self.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:reloadIndex inSection:0]];
vis.isActive = [NSNumber numberWithBool:NO];
NSError *error = nil;
if (![self.managedObjectContext save:amp;error])
{
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
[tableView reloadData];
}
Похоже, что это сработает, но это не так. Когда TableView перезагружается, 5 объектов возвращаются как соответствующие требованиям предиката, когда их должно быть только 4!
Что я здесь делаю не так? Что мне нужно сделать, чтобы обновить fetchedResultsController? Спасибо!
Ответ №1:
Возможно, вы захотите использовать методы делегирования для fetchedResultsController, которые должны делать все это за вас автоматически. Как только вы сохраните контекст, он обновит таблицу для вас, поэтому вам не придется вызывать [tableView reloadData]
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (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:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView endUpdates];
}
РЕДАКТИРОВАТЬ: если вы это сделаете, вам нужно будет реализовать следующий метод:
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
// configure cell;
}
Комментарии:
1. Спасибо за ваш ответ. На самом деле у меня реализованы все эти методы делегирования (включая configureCell (спасибо Рэю Вендерлиху)), и он по-прежнему не удаляет ячейку. Это сводит меня с ума!
2. Я предполагаю, что метод configureCell, а также ваши методы источника данных TableView основаны на fetchedResultsController? Я могу себе представить, почему это сводит вас с ума, если он все еще не обновляется, потому что похоже, что вы все делаете правильно. Вызов
[self.managedObjectContext save:amp;error]
должен автоматически обновлять все соответствующим образом. Просто некоторые вещи, которые я бы проверил: убедитесь, что managedObjectContext не равен null, и поместите некоторые журналы в методы делегирования, чтобы узнать, вызываются ли они. Кроме того, ничего из этого не происходит в фоновом потоке, не так ли? Это может привести к потере контекста, если это так
Ответ №2:
Я почти уверен, что добавление следующего кода в ваш метод seatedButton непосредственно перед строкой с [TableView reloadData] устранит проблему, но не уверен, согласно документу, что это должно быть необходимо.
if (![self.fetchedResultsController performFetch:amp;error])
{
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}