In my iOS app ,I have a table view with sections, all populated from core data.
If a delete the last row of a section, the app crashes.
I am not able to find out where is the issue in my code, you are kindly requested to help me. Thank you in advance. Here is my code so far:
#import "PersonsTVC.h"
#import "Person.h"
#implementation PersonsTVC
#synthesize fetchedResultsController = __fetchedResultsController;
#synthesize managedObjectContext = __managedObjectContext;
#synthesize selectedPerson;
#synthesize searchResults,titulosseccion;
- (void)setupFetchedResultsController
{
// 1 - Decide what Entity you want
NSString *entityName = #"Person"; // Put your entity name here
NSLog(#"Setting up a Fetched Results Controller for the Entity named %#", entityName);
// 2 - Request that Entity
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entityName];
// 3 - Filter it if you want
//request.predicate = [NSPredicate predicateWithFormat:#"Person.name = Blah"];
// 4 - Sort it if you want
// First sort descriptor (required for grouping into sections):
NSSortDescriptor *sortByDate = [[NSSortDescriptor alloc] initWithKey:#"date" ascending:NO];
// Second sort descriptor (for the items within each section):
NSSortDescriptor *sortByName = [[NSSortDescriptor alloc] initWithKey:#"firstname" ascending:YES];
[request setSortDescriptors:#[sortByDate, sortByName]];
//
// request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"firstname"
//ascending:YES
//selector:#selector(localizedCaseInsensitiveCompare:)]];
// 5 - Fetch it
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"sectionIdentifier"
cacheName:nil];
[self performFetch];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> theSection = [[self.fetchedResultsController sections] objectAtIndex:section];
NSString *sectionName = [theSection name];
if ([sectionName isEqualToString:#"0"]) {
return #"Today";
} else if ([sectionName isEqualToString:#"1"]) {
return #"Tomorrow";
}
else if ([sectionName isEqualToString:#"2"]) {
return #"Upcoming";
}
return #"Other";
}
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return [[self.fetchedResultsController sections] count];
}
- (void) viewDidLoad
{
self.searchResults = [NSMutableArray arrayWithCapacity:[[self.fetchedResultsController fetchedObjects] count]];
[self.tableView reloadData];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self setupFetchedResultsController];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Perform segue to detail when a SEARCH table cell is touched
if(tableView == self.searchDisplayController.searchResultsTableView)
{
[self performSegueWithIdentifier:#"Person Detail Segue" sender:tableView];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Persons Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
// Configure the cell...
Person *person = nil;
if (tableView == self.searchDisplayController.searchResultsTableView)
{
NSLog(#"Configuring cell to show search results");
person = [self.searchResults objectAtIndex:indexPath.row];
}
else
{
NSLog(#"Configuring cell to show normal data");
person = [self.fetchedResultsController objectAtIndexPath:indexPath];
}
NSString *fullname = [NSString stringWithFormat:#"%# %#", person.firstname, person.surname];
cell.textLabel.text = person.firstname;
if ([person.inRole.color isEqual :#"Yellow"])
{
cell.imageView.image = [UIImage imageNamed:#"Yellow"];
}
if ([person.inRole.color isEqual :#"Black"])
{
cell.imageView.image = [UIImage imageNamed:#"Black"];
}
if ([person.inRole.color isEqual :#"Grey"])
{
cell.imageView.image = [UIImage imageNamed:#"Grey"];
}
if ([person.inRole.color isEqual :#"Red"])
{
cell.imageView.image = [UIImage imageNamed:#"Red"];
}
if ([person.inRole.color isEqual :#"Blue"])
{
cell.imageView.image = [UIImage imageNamed:#"Blue"];
}
if ([person.inRole.color isEqual :#"Dark Green"])
{
cell.imageView.image = [UIImage imageNamed:#"DarkGreen"];
}
if ([person.inRole.color isEqual :#"Light Green"])
{
cell.imageView.image = [UIImage imageNamed:#"LightGreen"];
}
if ([person.inRole.color isEqual :#"Light Blue"])
{
cell.imageView.image = [UIImage imageNamed:#"LightBlue"];
}
if ([person.inRole.color isEqual :#"Brown"])
{
cell.imageView.image = [UIImage imageNamed:#"Brown"];
}
if ([person.inRole.color isEqual :#"Dark Orange"])
{
cell.imageView.image = [UIImage imageNamed:#"DarkOrange"];
}
NSDate *fechasinformat = person.date;
NSString *fecha0 = [NSString stringWithFormat:#"%#", fechasinformat];
cell.detailTextLabel.text = fecha0;
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView)
{
return [self.searchResults count];
}
else
{
return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];
}
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
[self.tableView beginUpdates]; // Avoid NSInternalInconsistencyException
// Delete the person object that was swiped
Person *personToDelete = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSLog(#"Deleting (%#)", personToDelete.firstname);
[self.managedObjectContext deleteObject:personToDelete];
[self.managedObjectContext save:nil];
// Delete the (now empty) row on the table
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self performFetch];
[self.tableView endUpdates];
}
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"Add Person Segue"])
{
NSLog(#"Setting PersonsTVC as a delegate of PersonDetailTVC");
PersonDetailTVC *personDetailTVC = segue.destinationViewController;
personDetailTVC.delegate = self;
NSLog(#"Creating a new person and passing it to PersonDetailTVC");
Person *newPerson = [NSEntityDescription insertNewObjectForEntityForName:#"Person"
inManagedObjectContext:self.managedObjectContext];
personDetailTVC.person = newPerson;
}
else if ([segue.identifier isEqualToString:#"Person Detail Segue"])
{
NSLog(#"Setting PersonsTVC as a delegate of PersonDetailTVC");
PersonDetailTVC *personDetailTVC = segue.destinationViewController;
personDetailTVC.delegate = self;
// Store selected Person in selectedPerson property
if(sender == self.searchDisplayController.searchResultsTableView)
{
NSIndexPath *indexPath = [self.searchDisplayController.searchResultsTableView indexPathForSelectedRow];
self.selectedPerson = [self.searchResults objectAtIndex:[indexPath row]];
}
else
{
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
self.selectedPerson = [self.fetchedResultsController objectAtIndexPath:indexPath];
}
NSLog(#"Passing selected person (%#) to PersonDetailTVC", self.selectedPerson.firstname);
personDetailTVC.person = self.selectedPerson;
}
else
{
NSLog(#"Unidentified Segue Attempted!");
}
}
- (void)theSaveButtonOnThePersonDetailTVCWasTapped:(PersonDetailTVC *)controller
{
// do something here like refreshing the table or whatever
// close the delegated view
[controller.navigationController popViewControllerAnimated:YES];
}
#pragma mark -
#pragma mark Content Filtering
-(void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
self.searchResults = [[self.fetchedResultsController fetchedObjects] filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
Person* person = evaluatedObject;
NSString* firstName = person.firstname;
//searchText having length < 3 should not be considered
if (!!searchText && [searchText length] < 3) {
return YES;
}
if ([scope isEqualToString:#"All"] || [firstName isEqualToString:scope]) {
return ([firstName rangeOfString:searchText].location != NSNotFound);
}
return NO; //if nothing matches
}]];
}
#pragma mark -
#pragma mark UISearchDisplayController Delegate Methods
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString scope:#"All"];
return YES;
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption
{
[self filterContentForSearchText:[self.searchDisplayController.searchBar text] scope:#"All"];
return YES;
}
#end
The error clearly indicates that you deleted 1 section, you had 1 before the deletion and you have 1 after the deletion. This is the problem. So probably your
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
returns an incorrect number of sections after deleting last object.
Either your [[self.fetchedResultsController sections] count] is not 0 after deleting last object, or numberOfSectionsInTableView:(UITableView *)tableView is called by the self.searchDisplayController.searchResultsTableView and you don't check for that.
EDIT: To solve the issue add the following code:
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
if(tableView==self.searchDisplayController.searchResultsTableView)
NSLog("search table asking");
else
NSLog("main table asking");
NSLog("fetched sections: %d", [[self.fetchedResultsController sections] count]);
return [[self.fetchedResultsController sections] count];
}
and post the console output when the crash occurs.
To implement the NSFetchedResultsController delegate methods add the code below to your viewController (I stripped some code out so check it compiles), and set the viewController as the delegate using something like _fetchedResultsController.delegate = self; when you create the controller (in setupFetchedResultsController).
Any time managedObjects that are in the fetchedResultsController result set are added, modified or deleted these methods are called. You can see that they include the necessary calls to the UITableView to update the display. These are used in conjunction with using the fetchedResultsController as the data source for the UITableView - that way you won't get any inconsistency errors.
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
NSLog(#"controllerWillChangeContent: called");
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{ NSLog(#"didChangeSection: called");
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
{
NSLog(#"controller:didChangeObject: called");
NSLog(#" object is %#", [anObject valueForKey:#"displayName"]);
UITableView *tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
{ NSLog(#"NSFetchedResultsChangeInsert: called");
[tableView insertRowsAtIndexPaths:#[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
}
break;
case NSFetchedResultsChangeDelete:
{ NSLog(#"NSFetchedResultsChangeDelete: called");
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
break;
case NSFetchedResultsChangeUpdate:
{ NSLog(#"NSFetchedResultsChangeUpdate: called");
[self configureCell:tableView cell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
}
break;
case NSFetchedResultsChangeMove:
{ NSLog(#"NSFetchedResultsChangeMove: called");
self.changedObjectIndex = newIndexPath;
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:#[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
}
break;
}
}
// NOTE: This is overridden by subclasses to modify the default behaviour
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
NSLog(#“controllerDidChangeContent: called");
[self.tableView endUpdates];
}
- (void)configureCell:(UITableView *)tableView cell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
NSManagedObject *object;
object = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [[object valueForKey:#"displayName"] description];
bool found =[[object valueForKey:#"found"] boolValue];
if (found) {
cell.textLabel.textColor = [UIColor greenColor];
}
}
A proper solution would be to make your PersonsTVC a delegate of the fetched results controller.
If you open an empty project that use CoreData you will have better understanding and probably find the solution to what you try to accomplish.
In its most simple form your code should be:
- (void)setupFetchedResultsController
{
//Setup your fetch request ...
NSFetchedResultsController* controller =
[[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"sectionIdentifier"
cacheName:nil];
//setup the delegation and retain the FRC
controller.delegate = self;
self.fetchedResultsController = controller;
NSError* error = nil;
if (![controller performFetch:&error]) {
NSLog(#"error performing fetch: %#",error);
abort();
}
//Not sure what this does, seems redundant
//[self performFetch];
}
Now you must access your managed object through the FRC you created (not sure why you need self.searchResults).
As I said, in the simplest form, implement:
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
// In the simplest, most efficient, case, reload the table view.
[self.tableView reloadData];
}
Note: your deletion code should be:
This is also the reason your application crashed, as you updated the table before the FRC was able to report changes in the context.
- (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);
abort();
}
}
}
Related
Whenever I attempt to enter a second character into the SearchBar the app crashes with a "terminating with uncaught exception" error. The SearchBar is referencing the Table Cell data if that helps at all. How do I fix this?
#interface DeviceViewController ()
#property (strong) NSMutableArray *devices;
#end
#implementation DeviceViewController
{
NSArray *searchResults;
}
- (NSManagedObjectContext *)managedObjectContext {
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];
//label.backgroundColor = [UIColor clearColor];
label.font = [UIFont fontWithName:#"HelveticaNeue-thin" size:28];
//label.shadowColor = [UIColor colorWithWhite:0.0 alpha:0.5];
label.textColor = [UIColor blackColor];
self.navigationItem.titleView = label;
label.text = #"TapNotes";
[label sizeToFit];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// Fetch the devices from persistent data store
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Device"];
self.devices = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
[self.tableView reloadData];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return [searchResults count];
} else {
return self.devices.count;
}
//return self.devices.count;
}
- (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]; }
NSManagedObject *device = [self.devices objectAtIndex:indexPath.row];
[cell.textLabel setText:[NSString stringWithFormat:#"%#", [device valueForKey:#"name"]]];
[cell.detailTextLabel setText:[NSString stringWithFormat:#"%#",[device valueForKey:#"version"]]];
if (tableView == self.searchDisplayController.searchResultsTableView) {
device = [searchResults objectAtIndex:indexPath.row];
} else {
[self.devices objectAtIndex:indexPath.row];
}
// cell.thumbnailImageView.image = [UIImage imageNamed:recipe.image];
return cell;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSManagedObjectContext *context = [self managedObjectContext];
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete object from database
[context deleteObject:[self.devices objectAtIndex:indexPath.row]];
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Can't Delete! %# %#", error, [error localizedDescription]);
return;
}
// Remove device from table view
[self.devices removeObjectAtIndex:indexPath.row];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
/*
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
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
}
}
*/
/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
*/
/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the item to be re-orderable.
return YES;
}
*/
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"UpdateDevice"]) {
NSManagedObject *selectedDevice = [self.devices objectAtIndex:[[self.tableView indexPathForSelectedRow] row]];
NSIndexPath *indexPath = nil;
if (self.searchDisplayController.active) {
indexPath = [self.searchDisplayController.searchResultsTableView indexPathForSelectedRow];
_devices = [searchResults objectAtIndex:indexPath.row];
} else {
indexPath = [self.tableView indexPathForSelectedRow];
_devices = [_devices objectAtIndex:indexPath.row];
}
DeviceDetailViewController *destViewController = segue.destinationViewController;
destViewController.device = selectedDevice;
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 55;
}
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"name contains[c] %#", searchText];
searchResults = [_devices filteredArrayUsingPredicate:resultPredicate];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString
scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
}
#end
You have many table view managing issues.
As I mentioned HERE, Use a NSFetchedResultsController (or open an empty project that include CoreData and see how easy table management will be when you have one).
You can't filter your search array without notifying the table that it needs to be reloaded.
The table view must know the count of the datasource as it will expect this data to exist when it asks for it in cellForRow...
Also, you treat your _device variable once as an NSArray and one as a managed object (in cellForRow...).
in cellForRow... change _device to device ...
and then you will have a new problem ...
(look at the advices I gave you in your previous question)
I am following Tim Roadley tutorial http://timroadley.com/2012/03/05/core-data-basics-part-7-search-bars/ trying to add a search bar to my project, but I am receiving an exception when executing my app. This is the exception:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier Cell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'.
And this is my code, updated with the tutorial code.
#import "ToDoItemsTableViewController.h"
#import "AppDelegate.h"
#import "AddToDoItemViewController.h"
#import "ToDoSubItemsTableViewController.h"
#interface ToDoItemsTableViewController ()
#property (nonatomic, strong)NSManagedObjectContext *managedObjectContext;
#property (nonatomic, strong)NSFetchedResultsController *fetchedResultsController;
#end
#implementation ToDoItemsTableViewController
#synthesize searchResults;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
-(NSManagedObjectContext *)managedObjectContext{
return [(AppDelegate*)[[UIApplication sharedApplication]delegate]managedObjectContext];
}
- (void)viewDidLoad
{
[super viewDidLoad];
//navigation bar background image
[self.navigationController.navigationBar
setBackgroundImage:[UIImage imageNamed:#"navBar.png"]
forBarMetrics:UIBarMetricsDefault];
NSDictionary *textAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
[UIColor blackColor],NSForegroundColorAttributeName,
[UIColor blackColor],NSBackgroundColorAttributeName,nil];
self.navigationController.navigationBar.titleTextAttributes = textAttributes;
NSError *error = nil;
if (![[self fetchedResultsController]performFetch:&error]){
NSLog(#"Error %#",error);
abort();
}
self.searchResults = [NSMutableArray arrayWithCapacity:[[self.fetchedResultsController fetchedObjects] count]];
[self.tableView reloadData];
}
-(void) viewWillAppear:(BOOL)animated{
[self.tableView reloadData];
}
- (void)viewDidUnload
{
self.searchResults = nil;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier]isEqualToString:#"addToDoItem"]){
UINavigationController *navigationController = segue.destinationViewController;
AddToDoItemViewController *addToDoItemViewController = (AddToDoItemViewController*)navigationController.topViewController;
ToDoItem *addToDoItem = [NSEntityDescription insertNewObjectForEntityForName:#"ToDoItem" inManagedObjectContext:self.managedObjectContext];
addToDoItem.todoDueDate = [NSDate date];
addToDoItemViewController.addToDoItem = addToDoItem;
}
if ([[segue identifier] isEqualToString:#"toToDoSubItems"]){
ToDoSubItemsTableViewController *todoSubItemsTableViewController = [segue destinationViewController];
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
ToDoItem *selectedToDoItem = (ToDoItem*)[self.fetchedResultsController objectAtIndexPath:indexPath];
todoSubItemsTableViewController.selectedToDoItem = selectedToDoItem;
}
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self.fetchedResultsController sections]count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView)
{
return [self.searchResults count];
}
else {
id<NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections]objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
ToDoItem *toDoItem = nil;
if (tableView == self.searchDisplayController.searchResultsTableView)
{
NSLog(#"Configuring cell to show search results");
toDoItem = [self.searchResults objectAtIndex:indexPath.row];
}
else
{
ToDoItem *todoItem = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = todoItem.todoName;
NSDate *fechaToDO = todoItem.todoDueDate;
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:#"EEEE, dd MMMM YYYY"];
NSString *fechaToDo = [dateFormatter stringFromDate:fechaToDO];
cell.detailTextLabel.text = fechaToDo;
}
return cell;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
static NSString *header = #"customHeader";
UITableViewHeaderFooterView *vHeader;
vHeader = [tableView dequeueReusableHeaderFooterViewWithIdentifier:header];
if (!vHeader) {
vHeader = [[UITableViewHeaderFooterView alloc] initWithReuseIdentifier:header];
vHeader.textLabel.backgroundColor = [UIColor redColor];
vHeader.textLabel.textColor = [UIColor whiteColor];
vHeader.contentView.backgroundColor = [UIColor redColor];
}
if (section == 0) {
vHeader.textLabel.backgroundColor = [UIColor redColor];
vHeader.textLabel.textColor = [UIColor whiteColor];
vHeader.contentView.backgroundColor = [UIColor redColor];
}
else if (section == 1) {
vHeader.textLabel.backgroundColor = [UIColor orangeColor];
vHeader.textLabel.textColor = [UIColor blueColor];
vHeader.contentView.backgroundColor = [UIColor orangeColor];
}
else if (section == 2) {
vHeader.textLabel.backgroundColor = [UIColor greenColor];
vHeader.textLabel.textColor = [UIColor whiteColor];
vHeader.contentView.backgroundColor = [UIColor greenColor];
}
vHeader.textLabel.text = [self tableView:tableView titleForHeaderInSection:section];
return vHeader;
}
-(NSString*)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
id <NSFetchedResultsSectionInfo> theSection = [[self.fetchedResultsController sections]objectAtIndex:section];
NSString *sectionname = [theSection name];
if ([sectionname isEqualToString:#"0"]){
NSString *valor = [NSString stringWithFormat:#"O V E R D U E (%d)", [self.tableView
numberOfRowsInSection:section]];
return valor;
}
else if ([sectionname isEqualToString:#"1"]){
NSString *valor = [NSString stringWithFormat:#"T O D A Y (%d)", [self.tableView
numberOfRowsInSection:section]];
return valor;
}
else if ([sectionname isEqualToString:#"2"]){
NSString *valor = [NSString stringWithFormat:#"U P C O M I N G (%d)", [self.tableView
numberOfRowsInSection:section]];
return valor;
}
if ([[self.fetchedResultsController sections]count]>0){
id<NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections]objectAtIndex:section];
return [sectionInfo name];
}
else{
return nil;
}
}
#pragma mark - Fetched Results Controller Section
-(NSFetchedResultsController*)fetchedResultsController{
if (_fetchedResultsController != nil){
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]init];
NSManagedObjectContext *context = self.managedObjectContext;
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ToDoItem" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]initWithKey:#"todoDueDate" ascending:YES];
NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc]initWithKey:#"todoName" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc]initWithObjects:sortDescriptor,sortDescriptor1, nil];
fetchRequest.sortDescriptors = sortDescriptors;
_fetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:#"sectionIdentifier" cacheName:nil];
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
#pragma mark - Fetched Results Controller Delegates
-(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:{
ToDoItem *changeToDoItem = [self.fetchedResultsController objectAtIndexPath:indexPath];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.textLabel.text = changeToDoItem.todoName;
NSDate *fechaToDO = changeToDoItem.todoDueDate;
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:#"EEEE, dd MMMM YYYY"];
NSString *fechaToDo = [dateFormatter stringFromDate:fechaToDO];
cell.detailTextLabel.text = fechaToDo;
}
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
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;
}
}
/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
*/
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
NSManagedObjectContext *context = [self managedObjectContext];
ToDoItem *ToDoItemToDelete = [self.fetchedResultsController objectAtIndexPath:indexPath];
[context deleteObject:ToDoItemToDelete];
NSError *error = nil;
if (![context save:&error]){
NSLog(#"Error: %#",error);
}
}
}
/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
*/
/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the item to be re-orderable.
return YES;
}
*/
/*
#pragma mark - Navigation
// In a story board-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
#pragma mark -
#pragma mark Content Filtering
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSLog(#"Previous Search Results were removed.");
[self.searchResults removeAllObjects];
for (ToDoItem *toDoItem in [self.fetchedResultsController fetchedObjects])
{
if ([scope isEqualToString:#"All"] || [toDoItem.todoName isEqualToString:scope])
{
NSComparisonResult result = [toDoItem.todoName compare:searchText
options:(NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch)
range:NSMakeRange(0, [searchText length])];
if (result == NSOrderedSame)
{
NSLog(#"Adding toDoItem.todoName '%#' to searchResults as it begins with search text '%#'", toDoItem.todoName, searchText);
[self.searchResults addObject:toDoItem];
}
}
}
}
#pragma mark -
#pragma mark UISearchDisplayController Delegate Methods
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString scope:#"All"];
return YES;
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption
{
[self filterContentForSearchText:[self.searchDisplayController.searchBar text] scope:#"All"];
return YES;
}
#end
Any advice or help is welcome.
Try to correct your code in this way :
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
you should notice that:
Important: You must register a class or nib file using the
registerNib:forCellReuseIdentifier: or
registerClass:forCellReuseIdentifier: method before calling this
method.
This is different method :
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
See documentation : https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/Reference/Reference.html#//apple_ref/occ/instm/UITableView/dequeueReusableCellWithIdentifier%3a
I would need to include the number of rows in the section title, like TODAY (6), that means that on section titled TODAY there are 6 rows (core data objects in my case). I know that it is to find here:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id<NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections]objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
But I don't know how to include this on each section to be shown as a label text.
Any advice is welcome....
#import "ToDoItemsTableViewController.h"
#import "AppDelegate.h"
#import "AddToDoItemViewController.h"
#import "ToDoSubItemsTableViewController.h"
#interface ToDoItemsTableViewController ()
#property (nonatomic, strong)NSManagedObjectContext *managedObjectContext;
#property (nonatomic, strong)NSFetchedResultsController *fetchedResultsController;
#end
#implementation ToDoItemsTableViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
-(NSManagedObjectContext *)managedObjectContext{
return [(AppDelegate*)[[UIApplication sharedApplication]delegate]managedObjectContext];
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSError *error = nil;
if (![[self fetchedResultsController]performFetch:&error]){
NSLog(#"Error %#",error);
abort();
}
}
-(void) viewWillAppear:(BOOL)animated{
[self.tableView reloadData];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier]isEqualToString:#"addToDoItem"]){
UINavigationController *navigationController = segue.destinationViewController;
AddToDoItemViewController *addToDoItemViewController = (AddToDoItemViewController*)navigationController.topViewController;
ToDoItem *addToDoItem = [NSEntityDescription insertNewObjectForEntityForName:#"ToDoItem" inManagedObjectContext:self.managedObjectContext];
addToDoItemViewController.addToDoItem = addToDoItem;
}
if ([[segue identifier] isEqualToString:#"toToDoSubItems"]){
ToDoSubItemsTableViewController *todoSubItemsTableViewController = [segue destinationViewController];
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
ToDoItem *selectedToDoItem = (ToDoItem*)[self.fetchedResultsController objectAtIndexPath:indexPath];
todoSubItemsTableViewController.selectedToDoItem = selectedToDoItem;
}
}
#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]objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
ToDoItem *todoItem = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = todoItem.todoName;
NSDate *fechaToDO = todoItem.todoDueDate;
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:#"EEEE, dd MMMM YYYY"];
NSString *fechaToDo = [dateFormatter stringFromDate:fechaToDO];
cell.detailTextLabel.text = fechaToDo;
return cell;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
static NSString *header = #"customHeader";
UITableViewHeaderFooterView *vHeader;
vHeader = [tableView dequeueReusableHeaderFooterViewWithIdentifier:header];
if (!vHeader) {
vHeader = [[UITableViewHeaderFooterView alloc] initWithReuseIdentifier:header];
vHeader.textLabel.backgroundColor = [UIColor redColor];
vHeader.textLabel.textColor = [UIColor whiteColor];
vHeader.contentView.backgroundColor = [UIColor redColor];
}
if (section == 1) {
vHeader.textLabel.backgroundColor = [UIColor blueColor];
vHeader.textLabel.textColor = [UIColor whiteColor];
vHeader.contentView.backgroundColor = [UIColor blueColor];
} else {
[vHeader setBackgroundColor:[UIColor redColor]];
}
vHeader.textLabel.text = [self tableView:tableView titleForHeaderInSection:section];
return vHeader;
}
-(NSString*)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
id <NSFetchedResultsSectionInfo> theSection = [[self.fetchedResultsController sections]objectAtIndex:section];
NSString *sectionname = [theSection name];
if ([sectionname isEqualToString:#"0"]){
return #"Overdue";
}
else if ([sectionname isEqualToString:#"1"]){
return #"Today";
}
else if ([sectionname isEqualToString:#"2"]){
return #"Upcoming";
}
if ([[self.fetchedResultsController sections]count]>0){
id<NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections]objectAtIndex:section];
return [sectionInfo name];
}
else{
return nil;
}
}
#pragma mark - Fetched Results Controller Section
-(NSFetchedResultsController*)fetchedResultsController{
if (_fetchedResultsController != nil){
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]init];
NSManagedObjectContext *context = self.managedObjectContext;
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ToDoItem" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]initWithKey:#"todoDueDate" ascending:YES];
NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc]initWithKey:#"todoName" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc]initWithObjects:sortDescriptor,sortDescriptor1, nil];
fetchRequest.sortDescriptors = sortDescriptors;
_fetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:#"sectionIdentifier" cacheName:nil];
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
#pragma mark - Fetched Results Controller Delegates
-(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:{
ToDoItem *changeToDoItem = [self.fetchedResultsController objectAtIndexPath:indexPath];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.textLabel.text = changeToDoItem.todoName;
cell.detailTextLabel.text = changeToDoItem.todoDescription;
}
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
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;
}
}
/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
*/
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
NSManagedObjectContext *context = [self managedObjectContext];
ToDoItem *ToDoItemToDelete = [self.fetchedResultsController objectAtIndexPath:indexPath];
[context deleteObject:ToDoItemToDelete];
NSError *error = nil;
if (![context save:&error]){
NSLog(#"Error: %#",error);
}
}
}
/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
*/
/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the item to be re-orderable.
return YES;
}
*/
/*
#pragma mark - Navigation
// In a story board-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
#end
[NSString stringWithFormat:#"Today(%d)", [tableView numberOfRowsInSection:
section]];
I am using core data in my iOS app. I have a tableview to show the data from an entity called persons. The entity persons has several attributes, on of them is "date". I would like to create 5 permanent sections in the table view (0.Today, 1.Tomorrow, 2.This week, 3.This month, 4.Upcoming), then I want the data to be shown on each section depending on the date.
I have been searching for this question, but nothing found that could be applied to my app.
I kindly request you to tell me the right way to handle it.
This is my code so far:
#import "PersonsTVC.h"
#import "Person.h"
#implementation PersonsTVC
#synthesize fetchedResultsController = __fetchedResultsController;
#synthesize managedObjectContext = __managedObjectContext;
#synthesize selectedPerson;
#synthesize searchResults,titulosseccion;
- (void)setupFetchedResultsController
{
// 1 - Decide what Entity you want
NSString *entityName = #"Person"; // Put your entity name here
NSLog(#"Setting up a Fetched Results Controller for the Entity named %#", entityName);
// 2 - Request that Entity
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entityName];
// 3 - Filter it if you want
//request.predicate = [NSPredicate predicateWithFormat:#"Person.name = Blah"];
// 4 - Sort it if you want
request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"firstname"
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)]];
// 5 - Fetch it
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
[self performFetch];
}
- (void) viewDidLoad
{
self.searchResults = [NSMutableArray arrayWithCapacity:[[self.fetchedResultsController fetchedObjects] count]];
[self.tableView reloadData];
}
-(void) viewDidUnload{
self.searchResults = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self setupFetchedResultsController];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[self.fetchedResultsController sections] count];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Perform segue to detail when a SEARCH table cell is touched
if(tableView == self.searchDisplayController.searchResultsTableView)
{
[self performSegueWithIdentifier:#"Person Detail Segue" sender:tableView];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Persons Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
// Configure the cell...
Person *person = nil;
if (tableView == self.searchDisplayController.searchResultsTableView)
{
NSLog(#"Configuring cell to show search results");
person = [self.searchResults objectAtIndex:indexPath.row];
}
else
{
NSLog(#"Configuring cell to show normal data");
person = [self.fetchedResultsController objectAtIndexPath:indexPath];
}
NSString *fullname = [NSString stringWithFormat:#"%# %#", person.firstname, person.surname];
cell.textLabel.text = person.firstname;
if ([person.inRole.color isEqual :#"Yellow"])
{
cell.imageView.image = [UIImage imageNamed:#"Yellow"];
}
if ([person.inRole.color isEqual :#"Black"])
{
cell.imageView.image = [UIImage imageNamed:#"Black"];
}
if ([person.inRole.color isEqual :#"Grey"])
{
cell.imageView.image = [UIImage imageNamed:#"Grey"];
}
if ([person.inRole.color isEqual :#"Red"])
{
cell.imageView.image = [UIImage imageNamed:#"Red"];
}
if ([person.inRole.color isEqual :#"Blue"])
{
cell.imageView.image = [UIImage imageNamed:#"Blue"];
}
if ([person.inRole.color isEqual :#"Dark Green"])
{
cell.imageView.image = [UIImage imageNamed:#"DarkGreen"];
}
if ([person.inRole.color isEqual :#"Light Green"])
{
cell.imageView.image = [UIImage imageNamed:#"LightGreen"];
}
if ([person.inRole.color isEqual :#"Light Blue"])
{
cell.imageView.image = [UIImage imageNamed:#"LightBlue"];
}
if ([person.inRole.color isEqual :#"Brown"])
{
cell.imageView.image = [UIImage imageNamed:#"Brown"];
}
if ([person.inRole.color isEqual :#"Dark Orange"])
{
cell.imageView.image = [UIImage imageNamed:#"DarkOrange"];
}
NSDate *fechasinformat = person.date;
NSString *fecha0 = [NSString stringWithFormat:#"%#", fechasinformat];
cell.detailTextLabel.text = fecha0;
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView)
{
return [self.searchResults count];
}
else
{
return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];
}
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
[self.tableView beginUpdates]; // Avoid NSInternalInconsistencyException
// Delete the person object that was swiped
Person *personToDelete = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSLog(#"Deleting (%#)", personToDelete.firstname);
[self.managedObjectContext deleteObject:personToDelete];
[self.managedObjectContext save:nil];
// Delete the (now empty) row on the table
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self performFetch];
[self.tableView endUpdates];
}
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"Add Person Segue"])
{
NSLog(#"Setting PersonsTVC as a delegate of PersonDetailTVC");
PersonDetailTVC *personDetailTVC = segue.destinationViewController;
personDetailTVC.delegate = self;
NSLog(#"Creating a new person and passing it to PersonDetailTVC");
Person *newPerson = [NSEntityDescription insertNewObjectForEntityForName:#"Person"
inManagedObjectContext:self.managedObjectContext];
personDetailTVC.person = newPerson;
}
else if ([segue.identifier isEqualToString:#"Person Detail Segue"])
{
NSLog(#"Setting PersonsTVC as a delegate of PersonDetailTVC");
PersonDetailTVC *personDetailTVC = segue.destinationViewController;
personDetailTVC.delegate = self;
// Store selected Person in selectedPerson property
if(sender == self.searchDisplayController.searchResultsTableView)
{
NSIndexPath *indexPath = [self.searchDisplayController.searchResultsTableView indexPathForSelectedRow];
self.selectedPerson = [self.searchResults objectAtIndex:[indexPath row]];
}
else
{
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
self.selectedPerson = [self.fetchedResultsController objectAtIndexPath:indexPath];
}
NSLog(#"Passing selected person (%#) to PersonDetailTVC", self.selectedPerson.firstname);
personDetailTVC.person = self.selectedPerson;
}
else
{
NSLog(#"Unidentified Segue Attempted!");
}
}
- (void)theSaveButtonOnThePersonDetailTVCWasTapped:(PersonDetailTVC *)controller
{
// do something here like refreshing the table or whatever
// close the delegated view
[controller.navigationController popViewControllerAnimated:YES];
}
#pragma mark -
#pragma mark Content Filtering
-(void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
self.searchResults = [[self.fetchedResultsController fetchedObjects] filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
Person* person = evaluatedObject;
NSString* firstName = person.firstname;
//searchText having length < 3 should not be considered
if (!!searchText && [searchText length] < 3) {
return YES;
}
if ([scope isEqualToString:#"All"] || [firstName isEqualToString:scope]) {
return ([firstName rangeOfString:searchText].location != NSNotFound);
}
return NO; //if nothing matches
}]];
}
#pragma mark -
#pragma mark UISearchDisplayController Delegate Methods
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString scope:#"All"];
return YES;
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption
{
[self filterContentForSearchText:[self.searchDisplayController.searchBar text] scope:#"All"];
return YES;
}
#end
You would have roughly to do the following:
Add a first sort descriptor on the date attribute to the fetch request.
Add a transient property "sectionIdentifier" to the Person entity,
and implement a custom getter - (NSString *)sectionIdentifier
to the Person managed object subclass, that returns
"0", "1", "2", "3", or "4", depending on the date attribute of the object.
Set sectionNameKeyPath:#"sectionIdentifier" in the creation of the fetched results controller.
Add a titleForHeaderInSection method to the table view controller, that
returns "Today", "Tomorrow", ... depending on the section.
The DateSectionTitles sample project from the Apple Developer Library also demonstrates how this works.
The sort descriptors would then look like this:
// First sort descriptor (required for grouping into sections):
NSSortDescriptor *sortByDate = [[NSSortDescriptor alloc] initWithKey:#"date" ascending:YES];
// Second sort descriptor (for the items within each section):
NSSortDescriptor *sortByName = [[NSSortDescriptor alloc] initWithKey:#"firstname" ascending:YES];
[request setSortDescriptors:#[sortByDate, sortByName]];
The getter method for the transient "sectionIdentifier" property would look like
(adapted from the "DateSectionTitles" sample code):
- (NSString *)sectionIdentifier
{
[self willAccessValueForKey:#"sectionIdentifier"];
NSString *tmp = [self primitiveValueForKey:#"sectionIdentifier"];
[self didAccessValueForKey:#"sectionIdentifier"];
if (!tmp)
{
NSDate *date = self.date;
// Using pseudo-code here:
if ("date is from today") {
tmp = #"0";
} else if ("date is from tomorrow") {
tmp = #"1";
} else ... // and so on ...
[self setPrimitiveValue:tmp forKey:#"sectionIdentifier"];
}
return tmp;
}
To determine if the date falls on today, tomorrow etc you have to use NSCalendar
methods.
The titleForHeaderInSection method would be similar to this (untested, as
everything else in this answer):
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> theSection = [[self.fetchedResultsController sections] objectAtIndex:section];
NSString *sectionName = [theSection name];
if ([sectionName isEqualToString:#"0"]) {
return #"Today";
} else if ([sectionName isEqualToString:#"1"]) {
return #"Tomorrow";
} ... // and so on ...
} else {
return #"Other";
}
}
I have a storyboard with core data project with 1 entity named "Password" and 4 string attributes, the data is connected to a table view with fecthedResultsController and the sections are set by one of the attributes calld "type".
When I launch my app every thing is o.k, I can add, update and delete and nothing is wrong.
Even when I go out of the app to the home screen and come back nothing happens.
The problem is when I close the app from the multitasking menu and re launch it, I get empty cells in the same amount of good ones.
they are grouped at the top with no section.
What am I doing wrong?
insertViewController.m
-(IBAction)insertPassword:(id)sender
{
NSInteger row;
NSString*subTypeSelectd=[[NSString alloc]init];
row = [pickerSubjects selectedRowInComponent:0];
subTypeSelectd = [arrSubSubjects objectAtIndex:row];
NSInteger row2;
NSString*TypeSelectd=[[NSString alloc]init];
row2 = [pickerSubjects selectedRowInComponent:1];
TypeSelectd = [arrSubject objectAtIndex:row2];
// self.insertPassword.desc=self.txtDesc.text;
// self.insertPassword.userName=self.txtUserName.text;
// self.insertPassword.password=self.txtPassword.text;
// self.insertPassword.type=TypeSelectd;
// self.insertPassword.subType=subTypeSelectd;
// NSLog(#"insertPassword");
// [self.delegete addPasswordViewControllerDidSave];
AppDelegate *delegate = [UIApplication sharedApplication].delegate;
NSManagedObjectContext *addViewcontrollerLocalContext = delegate.managedObjectContext;
NSManagedObject*password=[NSEntityDescription insertNewObjectForEntityForName:#"Password" inManagedObjectContext:addViewcontrollerLocalContext];
[password setValue:self.txtDesc.text forKey:#"desc"];
[password setValue:self.txtUserName.text forKey:#"userName"];
[password setValue:self.txtPassword.text forKey:#"password"];
[password setValue:subTypeSelectd forKey:#"subType"];
[password setValue:TypeSelectd forKey:#"type"];
NSError*error;
if(![addViewcontrollerLocalContext save:&error])
NSLog(#"input %#",error);
else NSLog(#"saved");
[self.navigationController popViewControllerAnimated:YES];
}
and the mainViewController.m
-(void)addPasswordViewControllerDidSave
{
NSManagedObjectContext*context=self.manageObjectContext;
NSError*error;
if(![context save:&error])
NSLog(#"error : %#",error);
NSLog(#"addPasswordViewControllerDidSave");
[self.navigationController popViewControllerAnimated:YES];
}
-(void)addPasswordViewControllerDidDelete:(Password *)passwordToDelete
{
NSLog(#"B");
NSManagedObjectContext*context=self.manageObjectContext;
[context deleteObject:passwordToDelete];
[self.navigationController popViewControllerAnimated:YES];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([[segue identifier]isEqualToString:#"addPassword"])
{
insertViewController*ipvc=(insertViewController*)[segue destinationViewController];
Password*p=(Password*)[NSEntityDescription insertNewObjectForEntityForName:#"Password" inManagedObjectContext:[self manageObjectContext]];
// ipvc.delegete=self;
ipvc.insertPassword=p;
}
if([[segue identifier]isEqualToString:#"updatePassword"])
{
updateViewController*uvc=(updateViewController*)[segue destinationViewController];
NSIndexPath*indexPath=[self.tableView indexPathForSelectedRow];
Password*p=[self.fecthedResultsController objectAtIndexPath:indexPath];
uvc.updatePassword=p;
}
}
//-------------------------------------------------------------table------------
-(NSFetchedResultsController*)fecthedResultsController
{
if(fecthedResultsController!=nil)
return fecthedResultsController;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Password"
inManagedObjectContext:[self manageObjectContext]];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"type" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
fecthedResultsController=[[NSFetchedResultsController
alloc]initWithFetchRequest:fetchRequest managedObjectContext:[self manageObjectContext]
sectionNameKeyPath:#"type" cacheName:nil];
fecthedResultsController.delegate=self;
return fecthedResultsController;
}
-(void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView beginUpdates];
[self.tableView reloadData];
}
-(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:{
Password*p=[self.fecthedResultsController objectAtIndexPath:indexPath];
UITableViewCell*cell=[tableview cellForRowAtIndexPath:indexPath];
cell.textLabel.text=p.userName;
cell.detailTextLabel.text=p.password;
// cell.imageView.image=[UIImage imageWithData: p.photodata];
}
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];
default:
break;
} }
-(NSString*)tableView:(UITableView *)tableView titleForHeaderInSection:
(NSInteger)section
{
return [[[self.fecthedResultsController sections]objectAtIndex:section]name];
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:
(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSManagedObjectContext*context=[self manageObjectContext];
Password*passwordToDelete=[self.fecthedResultsController objectAtIndexPath:indexPath];
[context deleteObject:passwordToDelete];
NSError*error;
if(![context save:&error])
NSLog(#"error");
} }
//-------------------------------------------------------------table-end-----------
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
-(void)viewWillApear
{
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSError*error;
if(![[self fecthedResultsController]performFetch:&error])
{
NSLog(#"ERROR");
abort();
}
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
//#warning Potentially incomplete method implementation.
// Return the number of sections.
NSLog(#"%i",[[self.fecthedResultsController sections]count]);
return [[self.fecthedResultsController sections]count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
//#warning Incomplete method implementation.
// Return the number of rows in the section.
id<NSFetchedResultsSectionInfo>secInfo=[[self.fecthedResultsController
sections]objectAtIndex:section];
return [secInfo numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
NSLog(#"cell");
Password*p=[self.fecthedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text=p.userName;
cell.detailTextLabel.text=p.password;
// if(p.photodata!=nil)
// cell.imageView.image=[UIImage imageWithData: p.photodata];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 60;
}
You have a typo in this line in your cellForRowAtIndexPath method:
it should be fetched ResultsController:
Password*p=[self.fecthedResultsController objectAtIndexPath:indexPath];
and again in your numberOfSectionInTableView:
NSLog(#"%i",[[self.fecthedResultsController sections]count]);
return [[self.fecthedResultsController sections]count];
and possibly in other methods.