У меня есть UICollectionViewController, это представление коллекции для некоторых объектов на карте.Таким образом, я могу обновить один элемент в выбранном результате (например, установить LIKE для одного из этого объекта).Это хорошо работает.Я вижу изменения немедленно.Но при попытке обновить свойство «Расстояние» всех Объектов, и одновременно контроллер отсортирован по этому свойству.Полученный результат не обновляется автоматически.И затем, во время прокрутки, повторно использованные ячейки не обновляются, и я вижу только те ячейки, которые были на дисплее, до того, как я начал обновлять свойство для всех объектов.Если бы я TouchUpInside первой ячейки в строке, например, это был бы объект 123, детализированная страница контроллера для другого объекта, этот объект, который должен оставаться в этой позиции после collectionView reloadData.
Incase I 'изменить дескрипторы сортировки с [fetchRequest setSortDescriptors:@[distanceAscending]];
на [fetchRequest setSortDescriptors:@[titleAscending]];
или [fetchRequest setSortDescriptors:@[titleAscending, distanceAscending]];
Это хорошо работает.
Incase Iменяю свойство «Расстояние» только одного объекта.Это хорошо работает.Контроллер сортирует Ячейки как следует.
Incase Я изменяю свойство "Дистанция" объектов, не выбранных на этот раз.Это хорошо работает.
Если я закрываю этот контроллер и снова открываю [fetchRequest setSortDescriptors:@[distanceAscending]];
, работаю как надо
Я пытаюсь перезагрузить CollectionViewController различными способами[self.collectionView reloadData];
и reloadwithPredicateDefault
.Но тот же результат.
Я пытаюсь изменить контекст управляемого объекта NSPrivateQueueConcurrencyType
и NSMainQueueConcurrencyType
.Но тот же результат.
MapObjectCollectionViewController.h
@interface MapObjectCollectionViewController : UICollectionViewController
MapObjectCollectionViewController.m
@interface MapObjectCollectionViewController ()<NSFetchedResultsControllerDelegate>
@property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;
@property (strong, nonatomic) NSManagedObjectContext* managedObjectContext;
@property (strong, nonatomic) NSPredicate * predicate1;
@property (strong, nonatomic) NSPredicate * predicate2;
#pragma mark - Fetched results controller
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription* description =
[NSEntityDescription entityForName:@"MapObj"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:description];
_predicate1 = [NSPredicate predicateWithFormat:@"types.typeObjValue IN %@", self.selectionsTypes];
_predicate2 = [NSPredicate predicateWithFormat:@"wiFi >= %i", 0];
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[_predicate1, _predicate2]];
[fetchRequest setPredicate:predicate];
NSSortDescriptor* titleAscending = [[NSSortDescriptor alloc] initWithKey:@"title" ascending:YES];
NSSortDescriptor* distanceAscending = [[NSSortDescriptor alloc] initWithKey:@"distance" ascending:NO];
[fetchRequest setSortDescriptors:@[distanceAscending]];
// [fetchRequest setSortDescriptors:@[titleAscending, distanceAscending]];
NSFetchedResultsController *aFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
return _fetchedResultsController;
}
-(void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath {
NSMutableDictionary *change = [[NSMutableDictionary alloc] init];
switch(type) {
case NSFetchedResultsChangeInsert:
change[@(type)] = newIndexPath;
break;
case NSFetchedResultsChangeDelete:
change[@(type)] = indexPath;
break;
case NSFetchedResultsChangeUpdate:
change[@(type)] = indexPath;
break;
case NSFetchedResultsChangeMove:
change[@(type)] = @[indexPath, newIndexPath];
break;
}
[_itemChanges addObject:change];
}
- (void)reloadwithPredicateDefault {
[NSFetchedResultsController deleteCacheWithName:nil];
self.fetchedResultsController = nil;
[self.fetchedResultsController performFetch:nil];
[self.collectionView reloadData];
}
#pragma mark - UICollectionViewDataSource
- (NSManagedObjectContext*) managedObjectContext {
if (!_managedObjectContext) {
_managedObjectContext = [[DataManager sharedManager] managedObjectContext];
}
return _managedObjectContext;
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller{
[self.collectionView performBatchUpdates:^{
for (NSDictionary *change in self->_sectionChanges) {
[change enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
NSFetchedResultsChangeType type = [key unsignedIntegerValue];
switch(type) {
case NSFetchedResultsChangeInsert:
[self.collectionView insertSections:[NSIndexSet indexSetWithIndex:[obj unsignedIntegerValue]]];
break;
case NSFetchedResultsChangeDelete:
[self.collectionView deleteSections:[NSIndexSet indexSetWithIndex:[obj unsignedIntegerValue]]];
break;
case NSFetchedResultsChangeMove:
break;
case NSFetchedResultsChangeUpdate:
break;
}
}];
}
for (NSDictionary *change in self->_itemChanges) {
[change enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
NSFetchedResultsChangeType type = [key unsignedIntegerValue];
switch(type) {
case NSFetchedResultsChangeInsert:
[self.collectionView insertItemsAtIndexPaths:@[obj]];
break;
case NSFetchedResultsChangeDelete:
[self.collectionView deleteItemsAtIndexPaths:@[obj]];
break;
case NSFetchedResultsChangeUpdate:
[self.collectionView reloadItemsAtIndexPaths:@[obj]];
break;
case NSFetchedResultsChangeMove:
[self.collectionView moveItemAtIndexPath:obj[0] toIndexPath:obj[1]];
break;
}
}];
}
} completion:^(BOOL finished) {
self->_sectionChanges = nil;
self->_itemChanges = nil;
}];
}
DataManager.h
@property (readonly, strong, nonatomic) NSManagedObjectContext *mainPrivateManagedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
+ (DataManager*)sharedManager;
DataManager.m
@implementation DataManager
@synthesize mainPrivateManagedObjectContext = _mainPrivateManagedObjectContext;
@synthesize managedObjectContext = _managedObjectContext;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
+(DataManager*) sharedManager{
static DataManager* manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[DataManager alloc] init];
});
return manager;
}
#pragma mark - Core Data stack
- (NSManagedObjectModel *)managedObjectModel {
// The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"ProjectName" withExtension:@"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
// The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it.
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
// Create the coordinator and store
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"ProjectName.sqlite"];
NSError *error = nil;
// NSString *failureReason = @"There was an error creating or loading the application's saved data.";
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:@{NSMigratePersistentStoresAutomaticallyOption:@YES, NSInferMappingModelAutomaticallyOption:@YES} error:&error]) {
NSLog(@"error = %@", error);
// Report any error we got.
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]; //Удалить старую базу
[_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]; //Создать базу заново
}
return _persistentStoreCoordinator;
}
- (NSManagedObjectContext *)managedObjectContext {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
if (_managedObjectContext != nil) {
// . NSLog(@"get managedObjectContext");
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (!coordinator) {
return nil;
}
_mainPrivateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_mainPrivateManagedObjectContext setPersistentStoreCoordinator:coordinator];
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_managedObjectContext setParentContext:_mainPrivateManagedObjectContext];
// . NSLog(@"get return managedObjectContext");
return _managedObjectContext;
}
- (NSManagedObjectContext *)getContextForBGTask {
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[context setParentContext:self.managedObjectContext];
return context;
}
- (NSArray*) allMapObj {
NSFetchRequest* request = [[NSFetchRequest alloc] init];
NSEntityDescription* description =
[NSEntityDescription entityForName:@"MapObj"
inManagedObjectContext:self.managedObjectContext];
[request setEntity:description];
NSError* requestError = nil;
NSArray* resultArray = [self.managedObjectContext executeFetchRequest:request error:&requestError];
if (requestError) {
NSLog(@"%@", [requestError localizedDescription]);
}
return resultArray;
}
- (void)calculateDistanceWithCurrentLoaction:(CLLocation*) currentLoaction{
NSFetchRequest* request = [[NSFetchRequest alloc] init];
NSManagedObjectContext * bgcontext = [self getContextForBGTask];
NSEntityDescription* description =
[NSEntityDescription entityForName:@"MapObj"
inManagedObjectContext:bgcontext];
[request setEntity:description];
NSError* requestError = nil;
NSArray* resultArray = [bgcontext executeFetchRequest:request error:&requestError];
if (requestError) {
NSLog(@"%@", [requestError localizedDescription]);
}
for (MapObj *mapObject in resultArray) {
CLLocation *endLocation = [[CLLocation alloc] initWithLatitude:[mapObject.latitude doubleValue] longitude:[mapObject.longitude doubleValue]];
CLLocationDistance distance = [currentLoaction distanceFromLocation:endLocation];
mapObject.distance = [NSDecimalNumber decimalNumberWithString:[NSString stringWithFormat:@"%f", distance]];
}
[bgcontext updatedObjects];
[self saveContextForBGTask:bgcontext];
}
- (void)saveContextForBGTask:(NSManagedObjectContext *)bgTaskContext {
if (bgTaskContext.hasChanges) {
[bgTaskContext performBlockAndWait:^{
NSError *error = nil;
[bgTaskContext save:&error];
}];
// Save default context
[self saveDefaultContext:YES];
}
}
- (void)saveDefaultContext:(BOOL)wait {
if (_managedObjectContext.hasChanges) {
[_managedObjectContext performBlockAndWait:^{
// . NSLog(@"managed context = %@", _managedObjectContext);
NSError *error = nil;
[self->_managedObjectContext save:&error];
}];
}
void (^saveMainPrivateManagedObjectContext) (void) = ^{
NSError *error = nil;
[self->_mainPrivateManagedObjectContext save:&error];
};
if ([_mainPrivateManagedObjectContext hasChanges]) {
if (wait){
// . NSLog(@"main context = %@", _mainPrivateManagedObjectContext);
[_mainPrivateManagedObjectContext performBlockAndWait:saveMainPrivateManagedObjectContext];
} else {
[_mainPrivateManagedObjectContext performBlock:saveMainPrivateManagedObjectContext];
}
}
}
Ошибка в консоли:
2019-09-28 12:35:14.951873+0400 ProjectName[15695:4020487] *** Assertion failure in -[UICollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:animator:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKitCore/UIKit-3698.140/UICollectionView.m:5972
CoreData: fault: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. attempt to perform an insert and a move to the same index path (<NSIndexPath: 0xa945f4d4afea737e> {length = 2, path = 0 - 4}) with userInfo (null)
Что я пропустил?