TableView or FetchedResultController Error - getting a blank row - ios

I have a simple app that create some object. Everything works fine but if i press the add button that open a detailViewController and there i will press cancel instead of saving it goes back to de mainTableView but it show a blank row, if i open the sql file there's no empty object so it should be an issue between the fetchedResultController and tableView loading it.
i know its not the best way to add an Object because in my add action i create an entity even if the user wont save. Yet it shouldnt show the blank row. Why is that happening? How to fix?
This is my code:
IngredietnViewController.m
#import "IngredientsTableViewController.h"
#interface IngredientsTableViewController ()
#end
#implementation IngredientsTableViewController
#synthesize managedObjectContext = _managedObjectContext;
#synthesize fetchedResultsController = _fetchedResultsController;
#synthesize ingredient = _ingredient;
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = #"Ingredients";
UIBarButtonItem *addButton = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(addIngredient)];
self.navigationItem.rightBarButtonItem = addButton;
self.navigationItem.leftBarButtonItem = self.editButtonItem;
self.tableView.tableFooterView = [[UIView alloc] init];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];
NSLog(#"%lu", (unsigned long)[sectionInfo numberOfObjects]);
return [sectionInfo numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
// Configure the cell...
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
[context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}
#pragma mark - Action
-(void)addIngredient{
DetailIngredientViewController *detailIngredientVC =[[DetailIngredientViewController alloc]initWithNibName:#"DetailIngredientViewController" bundle:nil];
detailIngredientVC.delegate = self;
self.ingredient = [NSEntityDescription insertNewObjectForEntityForName:#"Ingredient" inManagedObjectContext:self.managedObjectContext];
detailIngredientVC.detailIngredient = self.ingredient;
[self.navigationController pushViewController:detailIngredientVC animated:YES];
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{}
#pragma mark - Fetched Result
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Ingredient" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:NO];
NSArray *sortDescriptors = #[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"Master"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return _fetchedResultsController;
}
- (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:#[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:#[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView endUpdates];
}
#pragma mark - configureCell:
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
Ingredient *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = object.name ;
cell.detailTextLabel.text = object.rappresentation;
}
-(void)controller:(UIViewController *)controller didSaveIngredient:(Ingredient *)DetailIngredient{
NSLog(#"INGREDIENT SAVED");
NSError *error = nil;
if (![self.managedObjectContext save:&error]){
NSLog(#"Error %# %#", error, [error userInfo]);
}
[self.tableView reloadData];
}
#end
DeataiIngredientlViewController.m
#implementation DetailIngredientViewController
#synthesize tfNameIngredient, detailIngredient, tvDescription;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
UIBarButtonItem *saveButton = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemSave target:self action:#selector(saveIngredient)];
self.navigationItem.rightBarButtonItem = saveButton;
}
-(void)saveIngredient{
if ([self verifyIngredient] == YES){
detailIngredient.name = tfNameIngredient.text;
detailIngredient.rappresentation = tvDescription.text;
[self.delegate controller:self didSaveIngredient:detailIngredient];
[self.navigationController popToRootViewControllerAnimated:YES];
}
else{
NSLog(#"SBALLATA");
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"The Name and description are empty" delegate:self cancelButtonTitle:#"Back" otherButtonTitles:nil, nil];
[alert show];
}
}
-(BOOL)verifyIngredient{
if ([tfNameIngredient.text length] < 4 || [tvDescription.text length] < 4 ){
return NO;
}
else{
return YES;
}
}
#end

Your blank row corresponds to Ingredient NSManagedObject you created in addIngredient.
Even though you didn't save: the NSManagedObjectContext it still exists and is picked up by your NSFetchedResultsController.
When the user cancels the add you need to delete it. Perhaps add a controller:(UIViewController *)controller didCancelAddIngredient: method to your delegate?
ALSO, in controller:(UIViewController *)controller didSaveIngredient: you don't need to reloadData on your UITableView, that's what all that NSFetchedResultsControllerDelegate code is there to do for you.

Related

Having Trouble Reading From Core Data

I have an array that the user can add objects to. I have a table view that lists "bad" ingredients the user does not want in their food. They can add these objects in an array, but I don't think Im reading them properly. I know for sure I'm writing properly because I make sure that my code checks for it.
This is how I add objects in Core Data:
-(void)addRow
{
UIAlertView *myAlertView = [[UIAlertView alloc] initWithTitle:#"Add a Bad Ingredient" message:#"Type the name of the ingredient" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:#"Cancel", nil];
myAlertView.alertViewStyle = UIAlertViewStylePlainTextInput;
[myAlertView show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
NSManagedObjectContext *context = [self managedObjectContext];
AllergicIngredient *allergic = [NSEntityDescription insertNewObjectForEntityForName:#"AllergicIngredient" inManagedObjectContext:context];
NSString *enteredString = [[alertView textFieldAtIndex:0] text];
[allergic setValue:enteredString forKey:#"name"];
NSError *error;
if (![context save:&error])
{
NSLog(#"Couldnt find the save %#", error.localizedDescription);
}
else
{
NSLog(#"It saved properly");
}
[badIngredientsArray addObject:enteredString];
NSLog(#"%#", badIngredientsArray);
[self.tableView reloadData];
}
This is how I read from it (Making sure my array is getting Objects from core Data):
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"AllergicIngredient"];
badIngredientsArray = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
[self.tableView reloadData];
}
I'm struggling with finding the errors in how I get read from it. So far, I'm not given any error messages or SIGABRTS, because the app just crashed when I try to go to the specific page where I'm fetching the data.
I see you're using a UITableView with Core Data.
Given the context, why don't you use an NSFetchResultsController?
If you use that, you will then be able to perform the following:
#pragma mark - Fetched Results Controller Section
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:[MyMO description]
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Specify how the fetched objects should be sorted
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#""
ascending:YES
selector:#selector(localizedStandardCompare:)];
[fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sortDescriptor, nil]];
[fetchRequest setFetchBatchSize:20];
NSError *error = nil;
NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
NSLog(#"Error Fetching: %#", error);
}
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"masterCache"];
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView endUpdates];
}
- (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: {
Person *changedPerson = [self.fetchedResultsController objectAtIndexPath:indexPath];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.textLabel.text = changedPerson.birthName;
}
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
default:
break;
}
}
- (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;
default:
break;
}
}
And UITableView methods:
- (void)viewDidLoad
{
[super viewDidLoad];
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(#"Error while fetching: %#", error);
abort();
}
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return [self.fetchedResultsController.sections count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
id <NSFetchedResultsSectionInfo> sectionInfo = self.fetchedResultsController.sections[section];
return [sectionInfo numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
MyMo *mo = [self.fetchedResultsController objectAtIndexPath:indexPath];
// Configure the cell...
return cell;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [self.fetchedResultsController.sections[section]name];
}
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSManagedObjectContext *context = self.managedObjectContext;
MyMo *mo = [self.fetchedResultsController objectAtIndexPath:indexPath];
[context deleteObject:mo];
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Error saving: %#", error);
}
}
}

NSFetchedResultsController attempt to insert row 1 into section 0, but there are only 0 rows in section 0 after the update with userInfo (null)

i use NSFetchedResultsController with UITableViewController.
i successfully add new ocject to core data in separated view,
ParseStarterProjectAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSManagedObject *newWorkout;
newWorkout = [NSEntityDescription insertNewObjectForEntityForName:#"Workouts" inManagedObjectContext:context];
[newWorkout setValue:_workOutType forKey:#"type"];
[newWorkout setValue:_boostDate forKey:#"date"];
[newWorkout setValue:_workoutText forKey:#"text"];
[newWorkout setValue:_trainerLabelOutlet.text forKey:#"text"];
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}else{
NSLog(#"saved to core data");
}
but i get this error :
An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. attempt to insert row 1 into section 0, but there are only 0 rows in section 0 after the update with userInfo (null)
the code in the UITableViewController:
- (NSManagedObjectContext *)managedObjectContext {
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Workouts" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"date" ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil
cacheName:#"Root"];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
// Update to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
}
- (void)viewDidUnload {
self.fetchedResultsController = nil;
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
id sectionInfo = [[_fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
WorkoutObject *workout = [_fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = workout.text;
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyy-MM-dd HH:mm"];
NSString *dateString = [dateFormatter stringFromDate:workout.date];
cell.detailTextLabel.text = dateString;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
CustomTableViewCell *cell =[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Set up the cell...
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller is about to start sending change notifications, so prepare the table view for updates.
[self.tableView beginUpdates];
}
- (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)controller:(NSFetchedResultsController *)controller didChangeSection:(id )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)controllerDidChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller has sent all current change notifications, so tell the table view to process all updates.
[self.tableView endUpdates];
}
Does anyone know what the problem is?
i try to figure it out myself for the past 2 days but i incapable to.
thanks
I just had the same issue - in my case it turned out to be because the table view data source and delegates hadn't been assigned to the view controller on the storyboard in the outlets section of the attributes inspector. Hence numberOfRowsForSection and numberOfSectionsInTableView were not being called.

Assertion Failure in UITableView - Cant locate the problematic part

I have a tableviewcontroller and a fetchresultscontroller which fills the table with objects values. I am trying then to delete an object from the core data model as well as the row from the table through commitEditingStyle, but I get this error. I am aware of the error meaning, but I can't figure out what I need to fix in my code to make it disappear. Any help is appreciated.
Error:
EcoShoppingUI[1365:60b] *** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-2935.137/UITableView.m:1368
2014-07-25 15:03:42.137 EcoShoppingUI[1365:60b] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
favTable.m
#import "favTable.h"
#import "ecoAppDelegate.h"
#import "favCell.h"
#import "favButton.h"
#import "FavoritesInfo.h"
#interface favTable ()
#end
#implementation favTable
#synthesize tableView;
#synthesize managedObjectContext;
#synthesize fetchedResultsController;
- (NSFetchedResultsController *)fetchedResultsController {
if (!fetchedResultsController) {
self.managedObjectContext = ((ecoAppDelegate *) [UIApplication sharedApplication].delegate).managedObjectContext;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"FavoritesInfo" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil
cacheName:#"Root"];
fetchedResultsController = theFetchedResultsController;
fetchedResultsController.delegate = self;
}
return fetchedResultsController;
}
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = #"Favorites";
self.navigationController.navigationBar.translucent = NO;
self.managedObjectContext = ((ecoAppDelegate *) [UIApplication sharedApplication].delegate).managedObjectContext;
NSError *error;
if (![self.fetchedResultsController performFetch:&error]) {
// Update to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
}
- (void)viewDidUnload
{
[self setTableView:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.fetchedResultsController = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
#warning Potentially incomplete method implementation.
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
#warning Incomplete method implementation.
// Return the number of rows in the section.
id sectionInfo =
[[fetchedResultsController sections] objectAtIndex:section];
// NSLog(#"%zd", [fetchedResultsController.fetchedObjects count]); // amount of fetched objects
return [sectionInfo numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"favCell";
favCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[favCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
[self configureCell:cell atIndexPath:indexPath];
cell.cellmanu.text = #"hhjh";
cell.cellimage.image = [UIImage imageNamed:#"test1.jpg"];
return cell;
}
- (void)configureCell:(favCell *)cell
atIndexPath:(NSIndexPath *)indexPath
{
FavoritesInfo*favoritesInfo = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.cellname.text = favoritesInfo.name;
cell.cellscore.text = favoritesInfo.score;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
ecoAppDelegate *appDelegatee = (ecoAppDelegate *)[[UIApplication sharedApplication] delegate];
self.managedObjectContext = ((ecoAppDelegate *) [UIApplication sharedApplication].delegate).managedObjectContext;
FavoritesInfo*favoritesInfo = [self.fetchedResultsController objectAtIndexPath:indexPath];
[tableView
deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
[self.managedObjectContext deleteObject:favoritesInfo];
[appDelegatee saveContext]; // to save changes
NSError *error= nil;
if (![self.fetchedResultsController performFetch:&error]) {
// Update to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
[self.tableView reloadData];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
// FetchedResultsController update methods
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id )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];
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
/*
<#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:#"<#Nib name#>" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
*/
}
#end
Change to:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
ecoAppDelegate *appDelegatee = (ecoAppDelegate *)[[UIApplication sharedApplication] delegate];
FavoritesInfo*favoritesInfo = [self.fetchedResultsController objectAtIndexPath:indexPath];
[self.managedObjectContext deleteObject:favoritesInfo];
[appDelegatee saveContext]; // to save changes
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
It's hard to know what some bits of your code are doing, seeing as you are referencing your app delegate. Let me know what this does. it should sort the issue but i'm not 100%.
Your section header column should be the first attribute in sort order.

update CoreData Object not working correct

I'm new to iOS development/CoreData and have searched for an answer to my specific problem, but haven't found one yet.
I have a TableView with some Objects from my database. Each of the objects has an attribute "name". If I open the details view of one of the objects and go back to the tableView with the "back" Button everything works fine. It also works if I do no changes to the "name" TextField and press the "done" Button to get back to my tableView. As soon as I do some changes in the "name" TextField I get this error:
Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (9) must be equal to the number of rows contained in that section before the update (9), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).
Why is there an object being deleted?
Here is the code of my DetailsStaticTableViewController.m:
#import "WeinStaticTableViewController.h"
#import "Wein.h"
#import "JSMToolBox.h"
#interface WeinStaticTableViewController ()
#end
#implementation WeinStaticTableViewController
#synthesize nameTextView;
#synthesize landTextView;
#synthesize herstellerTextView;
#synthesize wein = _wein;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
if (self.wein != nil) {
self.nameTextView.text = self.wein.name;
}
}
- (void) viewDidDisappear:(BOOL)animated
{
[[JSMCoreDataHelper managedObjectContext] rollback];
[super viewDidDisappear:animated];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
[self setNameTextView:nil];
[self setLandTextView:nil];
[self setHerstellerTextView:nil];
self.wein = nil;
}
- (IBAction)barButtonItemDonePressed:(id)sender
{
NSLog(#"%s", __PRETTY_FUNCTION__);
if (self.wein != nil) {
self.wein.name = self.nameTextView.text;
[JSMCoreDataHelper saveManagedObjectContext:[JSMCoreDataHelper managedObjectContext]];
}
[self.navigationController popViewControllerAnimated:YES];
}
#end
Here is the code of my JSMCoreDataHelper.m:
#import "JSMCoreDataHelper.h"
#implementation JSMCoreDataHelper
+ (NSString*) directoryForDatabaseFilename
{
return [NSHomeDirectory() stringByAppendingString:#"/Library/Private Documents"];
}
+ (NSString*) databaseFileName
{
return #"database.sqlite";
}
+ (NSManagedObjectContext*) managedObjectContext
{
static NSManagedObjectContext *managedObjectContext;
if (managedObjectContext != nil) {
return managedObjectContext;
}
NSError *error;
// Verzeichnis unter dem das Datenbank-File abgespeichert werden soll, wird neu erzeugt, falls es noch nicht existiert.
[[NSFileManager defaultManager] createDirectoryAtPath:[JSMCoreDataHelper directoryForDatabaseFilename] withIntermediateDirectories:YES attributes:nil error:&error];
if (error) {
NSLog(#"Fehler: %#", error.localizedDescription);
return nil;
}
NSString *path = [NSString stringWithFormat:#"%#/%#", [JSMCoreDataHelper directoryForDatabaseFilename], [JSMCoreDataHelper databaseFileName]];
NSURL *url = [NSURL fileURLWithPath:path];
NSManagedObjectModel *managedModel = [NSManagedObjectModel mergedModelFromBundles:nil];
NSPersistentStoreCoordinator *storeCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:managedModel];
if (! [storeCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:&error]) {
NSLog(#"Fehler: %#", error.localizedDescription);
return nil;
}
managedObjectContext = [[NSManagedObjectContext alloc] init];
managedObjectContext.persistentStoreCoordinator = storeCoordinator;
return managedObjectContext;
}
+ (id) insertManagedObjectOfClass: (Class) aClass inManagedObjectContext: (NSManagedObjectContext*) managedObjectContext
{
NSManagedObject *managedObject = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass(aClass) inManagedObjectContext:managedObjectContext];
return managedObject;
}
+ (BOOL) saveManagedObjectContext: (NSManagedObjectContext*) managedObjectContext
{
NSError *error;
if (! [managedObjectContext save:&error]) {
NSLog(#"Fehler: %#", error.localizedDescription);
return NO;
}
return YES;
}
+ (NSArray*) fetchEntitiesForClass: (Class) aClass withPredicate: (NSPredicate*) predicate inManagedObjectContext: (NSManagedObjectContext*) managedObjectContext
{
NSError *error;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:NSStringFromClass(aClass) inManagedObjectContext:managedObjectContext];
fetchRequest.entity = entityDescription;
fetchRequest.predicate = predicate;
NSArray *items = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (error) {
NSLog(#"Fehler: %#", error.localizedDescription);
return nil;
}
return items;
}
+ (BOOL) performFetchOnFetchedResultsController: (NSFetchedResultsController*) fetchedResultsController
{
NSError *error;
if (! [fetchedResultsController performFetch:&error]) {
NSLog(#"Fehler: %#", error.localizedDescription);
return NO;
}
return YES;
}
#end
And finally the numberOfSections and numberOfRowsInSection of my MainTableViewController:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];
}
Any clue could help...thank you !
Heres my code using a NSFetchedResultsController, this works fine with your code for managing CoreData.
Hope this helps.
#import "Person.h"
#import "mainViewController.h"
#import "JSMCoreDataHelper.h"
#import "staticInfoViewController.h"
#implementation mainViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Load From CoreData
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
// Update to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
}
#pragma mark - TableView DataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
Person *p = [_fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [NSString stringWithFormat:#"Name: %#", p.name];
}
#pragma mark - TableView Delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
staticInfoViewController *info = [[staticInfoViewController alloc] initWithNibName:#"staticInfoViewController" bundle:[NSBundle mainBundle]];
info.currentPerson = [_fetchedResultsController objectAtIndexPath:indexPath];
[self.navigationController pushViewController:info animated:YES];
}
#pragma mark - Set NSFetchedResultsController
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Person" inManagedObjectContext:[JSMCoreDataHelper managedObjectContext]];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc]
initWithKey:#"name" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:[JSMCoreDataHelper managedObjectContext] sectionNameKeyPath:nil
cacheName:#"cache"];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
#pragma mark - NSFetchedResultsController Delegate
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller is about to start sending change notifications, so prepare the table view for updates.
[self.tableView beginUpdates];
}
- (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)controller:(NSFetchedResultsController *)controller didChangeSection:(id )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)controllerDidChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller has sent all current change notifications, so tell the table view to process all updates.
[self.tableView endUpdates];
}
#end
you have wrong number of data in UI and in core data. the row count dosnt match in UI and core data. you have daleted one row but UI/fetchresultcontroller does not know about and tries to read it from the database. either refetch after changes or implement the fetch result controller delegate:
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath;
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type;
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller;
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller;
do not forget to set fetch result controller to self.( viel spass )

NSFetchedResultsControllerDelegate methods not called from UIViewController

Just a caveat, this is my first ever question, be gentle with me.
I had a UITableViewController that conforms to the NSFetchedResultsControllerDelegate protocol. This all works perfectly. I want to change that to a UIViewController to be able to add more to the view than just the tableview. I created the tableView property and set the delegate and datasource. Everything works fine except that the FRC delegate methods are not called and the table is not updated. The inserts, edits, and deletes are being stored to the database, but the table is not updating when I change something in the context.
All the code is the basic template code for using a fetched results controller. When it sets the delegate in the line:
aFetchedResultsController.delegate = self;
It should be setting the delegate correctly, but it is not. Again, the only change I make is to remove the word Table from UITableViewController. It works with the UITableViewController but not the UIViewController. I have researched this extensively but have not found an answer.
Thanks.
Here's some code:
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
.
.
.
#class NowWhatDetailViewController;
#class Event;
#interface NowWhatMasterViewController : UITableViewController <NSFetchedResultsControllerDelegate, ChangeDateViewControllerDelgate, EditEventViewControllerDelgate, PasswordViewControllerDelegate,
UITableViewDelegate, UITableViewDataSource> {
NSMutableArray *saveButtonItems;
IBOutlet UILabel *nextEventLabel;
IBOutlet UILabel *timeToNextEventLabel;
}
#property (strong, nonatomic) NowWhatDetailViewController *detailViewController;
#property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;
#property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, weak) id <ChangeDateViewControllerDelgate> delegate;
#property (nonatomic, retain) IBOutlet UITableView *tableView;
.
.
.
#end
And the important sections of NowWhatMasterViewController.m
#import "NowWhatMasterViewController.h"
#interface NowWhatMasterViewController () {
}
#end
#implementation NowWhatMasterViewController {
Event *lastEditedEvent;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self performFetch];
[[self.splitViewController.viewControllers lastObject] topViewController];
self.detailViewController.viewNSDate = self.viewNSDate;
self.detailViewController.viewDate = self.viewDate;
self.detailViewController.viewSchedule = self.viewSchedule;
self.detailViewController.managedObjectContext = self.managedObjectContext;
self.detailViewController.fetchedResultsControllerDetail = self.fetchedResultsController;
[self.detailViewController updateDetailView];
// show the bottom toolbar
[self.navigationController setToolbarHidden:NO];
saveButtonItems = [self.toolbarItems mutableCopy];
nextEventLabel.text = #"next event";
timeToNextEventLabel.text = #"starts in ## minutes";
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
self.fetchedResultsController = nil;
}
- (void)viewDidUnload {
self.fetchedResultsController = nil;
}
#pragma mark - Table View
/*
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [dayEvents count];
}
*/
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
Event *event = [self.fetchedResultsController objectAtIndexPath:indexPath];
[self performSegueWithIdentifier:#"EditEvent" sender:event];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];
return [sectionInfo numberOfObjects];
//return [dayEvents count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"EventCell"];
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
if (_isLocked) {
return NO;
} else {
return YES;
}
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
[context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];
NSError *error = nil;
if (![context save:&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.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
// The table view should not be re-orderable.
return NO;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
Event *event = [self.fetchedResultsController objectAtIndexPath:indexPath];
[event toggleChecked];
[self configureCheckmarkForCell:cell withEvent:event];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
// this updates the detail controller when a row is selected. Not sure we want to do that
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
NSManagedObject *object = [[self fetchedResultsController] objectAtIndexPath:indexPath];
self.detailViewController.detailItem = object;
}
}
#pragma mark - Fetched results controller
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSLog(#"the fetched results controller is getting the events for %#", self.viewNSDate);
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Event" 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:#"eventNSDate" ascending:YES];
NSArray *sortDescriptors = #[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
// setup the predicate to return just the wanted date and schedule
NSPredicate *requestPredicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:#"(eventDate like '%#') AND (schedule.scheduleName like '%#')", [Event returnDateString:self.viewNSDate], self.viewSchedule.scheduleName]];
[fetchRequest setPredicate:requestPredicate];
// Clear out any previous cache
[NSFetchedResultsController deleteCacheWithName:#"Master"];
// 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:#"Master"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&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.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
// manually set the delegate?
return _fetchedResultsController;
}
- (void)performFetch
{
NSError *error;
if (![self.fetchedResultsController performFetch:&error]) {
//FATAL_CORE_DATA_ERROR(error);
NSLog(#"database error");
return;
}
}
- (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:#[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:#[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView endUpdates];
}

Resources