Reputation: 1839
I have got an app with a UICollectionView, the view displays custom UICollectionViewCells. When one of those cell´s register a tap, it opens a detail view in form of a model view controller. The collection view is filled with data from Core Data, it looks like this:
#pragma mark - Get Items From CoreData
-(NSArray*)refreshTableData
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Produkt"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError* error;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
/*for (Produkt *produkt in fetchedObjects)
{
NSLog(@"Name: %@", produkt.name);
}*/
return fetchedObjects;
}
#pragma mark - UICollectionView Datasource
- (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection: (NSInteger)section {
return [[self refreshTableData] count];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
CollectionViewCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"ItemCell" forIndexPath:indexPath];
Produkt* produkt = [[self refreshTableData]objectAtIndex:indexPath.row];
[cell.collectionLabel setText:produkt.name];
[cell.collectionImage setImage:[[UIImage alloc]initWithData:produkt.image]];
[cell.layer setCornerRadius:10];
[cell.layer setMasksToBounds:YES];
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = cell.bounds;
gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor blackColor] CGColor], (id)[[UIColor whiteColor] CGColor], nil];
[cell.layer insertSublayer:gradient atIndex:0];
return cell;
}
#pragma mark UICollectionViewDelegate
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
Produkt* produkt = [[self refreshTableData]objectAtIndex:indexPath.row];
produktView = [[ProduktViewController alloc]
initWithNibName:@"ProduktViewController"
bundle:nil];
produktView.modalPresentationStyle = UIModalPresentationFormSheet;
produktView.delegate = self;
produktView.produkt = produkt;
[self presentViewController:produktView animated:YES completion:nil];
}
- (UIEdgeInsets)collectionView:
(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
return UIEdgeInsetsMake(50, 20, 50, 20);
}
When I create a new Produkt Object(NSManagedObject), save it and reload the UICollectionView everything works fine. Thats the code for creating a new Produkt-Object:
-(void)selectedItem:(NSString *)selectedItem
{
//Dismiss the popover if it's showing.
if (itemPickerPopover) {
[itemPickerPopover dismissPopoverAnimated:YES];
itemPickerPopover = nil;
}
if([selectedItem isEqualToString:@"Produkt"])
{
NSLog(@"SELECTED ITEM:%@",selectedItem);
produktView = [[ProduktViewController alloc]
initWithNibName:@"ProduktViewController"
bundle:nil];
Produkt* newProdukt = [NSEntityDescription
insertNewObjectForEntityForName:@"Produkt"
inManagedObjectContext:context];
produktView.modalPresentationStyle = UIModalPresentationFormSheet;
produktView.delegate = self;
produktView.context = context;
produktView.produkt = newProdukt;
[self presentViewController:produktView animated:YES completion:nil];
}
}
Now when I select a cell and the detail view pops up, and I edit the data in the detail view, and afterwards save it, with this function(contained in the detail view class):
-(void)saveChanges
{
NSLog(@"SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE ");
produkt.name = name.text;
produkt.artikelNr = artikelNr.text;
produkt.gekauftBei = bestelltBei.text;
produkt.gebrauchsAnweisung = gebrauchsAnleitung.text;
NSData *savedImageData = UIImagePNGRepresentation(image.image);
produkt.image = savedImageData;
NSError *error;
[context save:&error];
}
When the detail controller dismisses this delegate function gets called:
#pragma mark ProduktView delegate
-(void)didDismissPresentedViewController
{
NSLog(@"DISMISS CONTROLLER");
[produktView dismissViewControllerAnimated:YES completion:nil];
[collectionView reloadData];
}
Now here comes the problem: When I save a changed object and reload the table view, the presented cells are associated to the wrong entities. For example, the collection view shows the objects: 1 , 2 , 3 , 4 but when I click for example on object 3 the detail view shows the object 1. After I dismissed the view controller, the collection view changes the order again. When I restart the app, the changes I made to an object, are not saved.
In the main view controller, I have created one NSMangedObjectContext which I pass around to the other controllers, so I always stay in the same context.
AppDelegate *appDelegate =
[[UIApplication sharedApplication] delegate];
context =
[appDelegate managedObjectContext];
Thanks for your help.
Upvotes: 1
Views: 2110
Reputation: 539965
Without a sort descriptor, the order of the objects returned by a fetch request is undefined. It is not even guaranteed that two identical fetch requests return the objects in the same order.
Since all of your collection view data source methods execute a new fetch request (which is extremely ineffective!) nothing can be said about which object
Produkt* produkt = [[self refreshTableData]objectAtIndex:indexPath.row];
in cellForItemAtIndexPath
actually returns.
So one possible solution would be to fetch the objects once into an NSArray
and use
this array as collection view data source. If object have been added or modified,
execute a new fetch request and reload the collection view.
Another possible solution would be to use a NSFetchedResultsController
(FRC).
A FRC tracks changes to the managed object context automatically and calls
delegate functions if objects are inserted, deleted or modified. If you choose that
path, you should note that using a FRC with a collection view is a bit more tricky
than with a table view as far as automatic animated updates as concerned. But
this article http://ashfurrow.com/blog/uicollectionview-example might help.
Upvotes: 2