Search bar implementation not working - ios

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

Related

UITableView sections are not ordered as expected

I am using a tableView with customised section titles. The core data objects are shown on a precise section depending on the value from a transient attribute called sectionIdentifier. Everything is working as expected, but the order of the sections is not responding as I expected. This should be the sections order:
1. OVERDUE, sectionIdentifier = 0
2. TODAY, sectionIdentifier = 1
3. TOMORROW, sectionIdentifier = 2
4. UPCOMING, sectionIdentifier = 3
5. SOMEDAY, sectionIdentifier = 4
At this app state, the section order is as shown in the image below:
Any help is welcome to explain this behaviour and to find the way to obtain the desired section order.
Here is my code to help you find the issue.
#import "ToDoItemsTableViewController.h"
#import "AppDelegate.h"
#import "AddToDoItemViewController.h"
#import "ToDoSubItemsTableViewController.h"
#interface ToDoItemsTableViewController ()<UIAlertViewDelegate>
#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 whiteColor],NSForegroundColorAttributeName,
[UIColor whiteColor],NSBackgroundColorAttributeName,nil];
self.navigationController.navigationBar.titleTextAttributes = textAttributes;
self.tableView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"navBar"]];
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
{
if (tableView == self.searchDisplayController.searchResultsTableView)
{
return 1;
}
else
{
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];
// Configure the cell...
ToDoItem *toDoItem = nil;
if (tableView == self.searchDisplayController.searchResultsTableView)
{
if (cell==nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
cell.accessoryType=UITableViewCellAccessoryDisclosureIndicator;
}
NSLog(#"Configuring cell to show search results");
toDoItem = [self.searchResults objectAtIndex:indexPath.row];
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;
}
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];
}
else if (section == 3) {
vHeader.textLabel.backgroundColor = [UIColor greenColor];
vHeader.textLabel.textColor = [UIColor whiteColor];
vHeader.contentView.backgroundColor = [UIColor greenColor];
}
else if (section == 4) {
vHeader.textLabel.backgroundColor = [UIColor blueColor];
vHeader.textLabel.textColor = [UIColor whiteColor];
vHeader.contentView.backgroundColor = [UIColor blueColor];
}
vHeader.textLabel.text = [self tableView:tableView titleForHeaderInSection:section];
return vHeader;
}
-(NSString*)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
if (tableView == self.searchDisplayController.searchResultsTableView){
NSString *valor = [NSString stringWithFormat:#"S E A R C H R E S U L T S (%d)",[self.searchResults count]];
return valor;
}
else {
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:#"T O M O R R O W (%d)", [self.tableView
numberOfRowsInSection:section]];
return valor;
}
else if ([sectionname isEqualToString:#"3"]){
NSString *valor = [NSString stringWithFormat:#"U P C O M I N G (%d)", [self.tableView
numberOfRowsInSection:section]];
return valor;
}
else if ([sectionname isEqualToString:#"4"]){
NSString *valor = [NSString stringWithFormat:#"S O M E D A Y (%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 NO;
}
// Override to support editing 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;
}
*/
/*
#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 {
self.searchResults = [[self.fetchedResultsController fetchedObjects] filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
ToDoItem * item = evaluatedObject;
NSString* name = item.todoName;
//searchText having length < 3 should not be considered
if (!!searchText && [searchText length] < 3) {
return YES;
}
if ([scope isEqualToString:#"All"] || [name isEqualToString:scope]) {
return ([name 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;
}
- (IBAction)quickAddAction:(UIButton *)sender {
UIAlertView * alert =[[UIAlertView alloc ] initWithTitle:#"Add Task" message:#"Enter the task name" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles: nil];
alert.alertViewStyle = UIAlertViewStylePlainTextInput;
[alert addButtonWithTitle:#"Add"];
[alert show];
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 1)
{
UITextField *todoText = [alertView textFieldAtIndex:0];
NSLog(#"username: %#", todoText.text);
ToDoItem *addToDoItem = [NSEntityDescription insertNewObjectForEntityForName:#"ToDoItem" inManagedObjectContext:self.managedObjectContext];
addToDoItem.todoDueDate = [NSDate date];
NSString *todoConvertido = todoText.text;
addToDoItem.todoName = todoConvertido;
[self.tableView reloadData];
}
}
#end
you sort only based on due Date and name so the category with the oldest entry comes first.
the entries would have to be sorted based on your sectionIdentifier first
NSSortDescriptor *sortDescriptor0 = [[NSSortDescriptor alloc]initWithKey:#"sectionIdentifier" ascending:YES];
NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc]initWithKey:#"todoDueDate" ascending:YES];
NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc]initWithKey:#"todoName" ascending:YES];

Putting number of rows in section title

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]];

After deleting last row of a section, the app crashes

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();
}
}
}

[UIBarButtonItem _isAncestorOfFirstResponder]: unrecognized selector sent to instance

This code works perfectly in one app and crashes in another with message:
[UIBarButtonItem _isAncestorOfFirstResponder]: unrecognized selector sent to instance
If I comment the magical record code I can see that method runs OK.
- (void)viewDidLoad{
[super viewDidLoad];
UIBarButtonItem *addButton = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(addNote:)];
self.navigationItem.rightBarButtonItem = addButton;
}
- (IBAction)addNote:(id)sender{
NSLog(#"%s",__PRETTY_FUNCTION__);
NSManagedObjectContext *localContext = [NSManagedObjectContext MR_contextForCurrentThread];
Note *note = [Note MR_createInContext:localContext];
NSDate *now = [NSDate date];
note.date = now;
note.person = _person;
[localContext MR_saveWithOptions:MRSaveSynchronously completion:^(BOOL success, NSError *error) {
UIStoryboard *sb = [UIStoryboard storyboardWithName:NSBundle.mainBundle.infoDictionary[#"UIMainStoryboardFile"] bundle:NSBundle.mainBundle];
NoteViewController *vc = [sb instantiateViewControllerWithIdentifier:#"NoteViewController"];
vc.note = note;
[self.navigationController pushViewController:vc animated:YES];
}];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
UILabel *noResultsLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, self.tableView.bounds.size.width, self.tableView.bounds.size.width)];
noResultsLabel.textColor = [UIColor colorWithWhite:0.557 alpha:1.000];
noResultsLabel.font = [UIFont boldSystemFontOfSize:35];
noResultsLabel.textAlignment = NSTextAlignmentCenter;
noResultsLabel.text = NSLocalizedString(#"No notes", nil);
self.tableView.nxEV_emptyView = noResultsLabel;
self.fetchedResultsController.delegate = self;
[self.fetchedResultsController performFetch:nil];
[self.tableView reloadData];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
_fetchedResultsController.delegate = nil;
}
#pragma mark - Fetched results controller
- (void)dealloc
{
NSLog(#"%s",__PRETTY_FUNCTION__);
self.fetchedResultsController.delegate = nil;
self.fetchedResultsController = nil;
}
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"person == %#", _person];
self.fetchedResultsController = [Note MR_fetchAllGroupedBy:#"source" withPredicate:predicate sortedBy:#"source,date" ascending:YES delegate:self];
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)selectCreatedRowatIndexPath:(NSIndexPath *)indexPath{
[self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionMiddle];
}
- (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:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:(NoteCell *)[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationAutomatic];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView endUpdates];
[self.tableView reloadData];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];
if (sectionInfo.name.intValue == 0) {
return NSLocalizedString(#"Not Secret", #"Notes");
}else return NSLocalizedString(#"Secret", #"Notes");
}
- (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 = #"Note Cell";
NoteCell *cell = (NoteCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(NoteCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
Note *note = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.dateLabel.text = [[CZDateFormatterCache mainQueueCache] localizedStringFromDate:note.date dateStyle:kCFDateFormatterLongStyle timeStyle:kCFDateFormatterShortStyle];
if (note.note.length > 0) {
cell.noteLabel.text = note.note;
} else {
cell.noteLabel.text = NSLocalizedString(#"New Note", nil);
}
cell.rateView.rate = [note.trustworthy intValue];
NSLog(#"note.trustworthy intValue %i", [note.trustworthy intValue]);
cell.rateView.backgroundColor = [UIColor clearColor];
cell.backgroundColor = [UIColor whiteColor];
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
[MagicalRecord saveUsingCurrentThreadContextWithBlock:^(NSManagedObjectContext *localContext) {
[[self.fetchedResultsController objectAtIndexPath:indexPath] MR_deleteInContext:localContext];
} completion:^(BOOL success, NSError *error) {
//
}];
}
}
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
return NO;
}
#pragma mark - Segue
- (void)prepareForSegue:(UIStoryboardSegue *)segue
sender:(id)sender {
if ([segue.identifier isEqualToString:#"Note Segue"]) {
NSLog(#"%s",__PRETTY_FUNCTION__);
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
Note *note = [[self fetchedResultsController] objectAtIndexPath:indexPath];
NoteViewController *anvc = segue.destinationViewController;
anvc.note = note;
}
}
OK, the problem was in the second view controller.
I found it occasionally.
I used UIBarButtonItem with custom view as a navigation bar titleView.

Tableview get index out of bounds with empty array

I have a tableview that is filled with a fetchedresultscontroller and a searchbar. Up until now while creating the app i have only been using 7 cells and have worked great but since i started using more than 7 cells i get the error of out of bounds empty array when i touch the searchbar. but when i return it back to 7 cells it works perfect.
What could possibly be wrong?
Error:
erminating app due to uncaught exception 'NSRangeException', reason: '* -[__NSArrayM objectAtIndex:]: index 2 beyond bounds for empty array'
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = #"Exercises";
id appDelegate = [[UIApplication sharedApplication] delegate];
_managedObjectContext = [appDelegate managedObjectContext];
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
NSLog(#"%# %#", error, [error userInfo]);
abort();
}
_searchResults = [[NSMutableArray alloc] init];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
NSIndexPath *selectedIndexPath = [_tableView indexPathForSelectedRow];
if (!selectedIndexPath) {
[_tableView setContentOffset:CGPointMake(0, 44) animated:NO];
}
else {
[_tableView deselectRowAtIndexPath:selectedIndexPath animated:YES];
}
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
if (self.searchDisplayController.isActive) {
[self.searchDisplayController setActive:NO animated:YES];
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
if (self.searchDisplayController.isActive) {
return 1;
}
return [[_fetchedResultsController sections] count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
if (self.searchDisplayController.isActive) {
return nil;
}
return [[[_fetchedResultsController sections] objectAtIndex:section] name];
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 22)];
view.backgroundColor = [UIColor colorWithRed:20.0/255.0 green:20.0/255.0 blue:20.0/255.0 alpha:0.8];
UILabel *sectionLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 0, 160, 22)];
sectionLabel.font = [UIFont fontWithName:#"Avenir-Book" size:16.0f];
sectionLabel.text = #"Search Results";
if (!self.searchDisplayController.isActive) {
sectionLabel.text = [[[_fetchedResultsController sections] objectAtIndex:section] name];
}
sectionLabel.backgroundColor = [UIColor clearColor];
sectionLabel.textColor = [UIColor whiteColor];
[view addSubview:sectionLabel];
return view;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (self.searchDisplayController.isActive) {
return [_searchResults count];
}
return [[[_fetchedResultsController sections] objectAtIndex:section] numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"exerciseCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
[self stampCell:cell atIndexPath:indexPath];
}
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
#pragma mark - Cell
- (void)stampCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
if ([cell.reuseIdentifier isEqualToString:#"exerciseCell"]) {
UILabel *textLabel = [[UILabel alloc] initWithFrame:CGRectMake(70, 2, 150, 22)];
textLabel.tag = kExerciseCellTextLabel;
textLabel.font = [UIFont systemFontOfSize:18.0];
textLabel.textColor = [UIColor blackColor];
textLabel.highlightedTextColor = [UIColor whiteColor];
textLabel.backgroundColor = [UIColor clearColor];
[cell.contentView addSubview:textLabel];
UILabel *detailTextLabel = [[UILabel alloc] initWithFrame:CGRectMake(70, 24, 150, 18)];
detailTextLabel.tag = kExerciseCellDetailTextLabel;
detailTextLabel.font = [UIFont systemFontOfSize:14.0];
detailTextLabel.textColor = [UIColor colorWithRed:128.0/255.0 green:128.0/255.0 blue:128.0/255.0 alpha:1.0];
detailTextLabel.highlightedTextColor = [UIColor colorWithRed:127.0/255.0 green:127.0/255.0 blue:127.0/255.0 alpha:1.0];
detailTextLabel.backgroundColor = [UIColor clearColor];
[cell.contentView addSubview:detailTextLabel];
}
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
if ([cell.reuseIdentifier isEqualToString:#"exerciseCell"]) {
UILabel *textLabel = (UILabel *)[cell.contentView viewWithTag:kExerciseCellTextLabel];
UILabel *detailTextLabel = (UILabel *)[cell.contentView viewWithTag:kExerciseCellDetailTextLabel];
Exercise *exercise;
if (self.searchDisplayController.isActive) {
exercise = [_searchResults objectAtIndex:indexPath.row];
}
else {
exercise = [_fetchedResultsController objectAtIndexPath:indexPath];
}
textLabel.text = [exercise valueForKey:kExerciseName];
detailTextLabel.text = [NSString stringWithFormat:#"%# - %#", [[exercise valueForKey:kExerciseEType] valueForKey:kExerciseTypeName], [[exercise valueForKey:kExerciseGear] valueForKey:kGearName]];
cell.selectionStyle = UITableViewCellSelectionStyleGray;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
}
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"exerciseDetail" sender:self];
}
#pragma mark - UISearchBarDelegate
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[_searchResults removeAllObjects];
if (searchString.length > 0) {
for (Exercise *exercise in [_fetchedResultsController fetchedObjects]) {
NSRange range = [[exercise valueForKey:kExerciseName] rangeOfString:searchString options:NSCaseInsensitiveSearch];
if (range.location == 0) {
[_searchResults addObject:exercise];
}
}
}
return YES;
}
- (BOOL)searchBarShouldBeginEditing:(UISearchBar*)searchBar {
[super setPan];
return YES;
}
- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar
{
[super setPan];
return YES;
}
#pragma mark - NSFetchedResultsControllerDelegate
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController) {
return _fetchedResultsController;
}
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:kExercise inManagedObjectContext:_managedObjectContext];
[request setEntity:entity];
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:kExerciseName ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObject:sort]];
NSFetchedResultsController *fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:_managedObjectContext sectionNameKeyPath:kExerciseFirstLetter cacheName:#"ExerciseList"];
[self setFetchedResultsController:fetchedResultsController];
[_fetchedResultsController setDelegate:self];
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];
}
#end
It shows like some of array remains empty..the better way to check it
-Put NSLog statement after array element and run it..IT shows which array remains empty.
By doing so if your problem doesn't get solved..please let me know..so,i can help you further..
Where do you see the crash - when showing just search results or when showing the entire list?
If its just search results, then your _searchResults does not have more than 7 records.
If it is when you are showing all results, then check the number of records in each value of _fetchedResultsController.sections. Looks like they don't have more than 7 records.
Hi i guess ur removing all elements from _searchResults may be due to this the app is crashing.please check this once.
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[_searchResults removeAllObjects];

Resources