I have implemented a Section Index for my tableview. It returns the correct letters for what is stored in coredata (A B C D F G H I K M N O P Q R S T U V W). However the indexes are off.
If I click on the letter M it takes me to letter I, etc.. The name of the index I am attempting to sort on is name
What is there for me to do to fix the index?
I am also implementing numberOfSectionsInTableView,numberOfRowsInSection, and titeForHeaderInSection. The section headers are displayed correctly.
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
NSArray * sectionTitlesArray = [fetchedResultsController sectionIndexTitles];
NSMutableArray *newTitles = [[NSMutableArray alloc] init];
for (NSString *state in sectionTitlesArray) {
[newTitles addObject:[NSString stringWithFormat:#"%#", state]];
}
return [newTitles autorelease];
}
Including the FRC just in case it is relevant:
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController == nil) {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"WidgetCoItems" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:#"state" ascending:YES];
NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor1,sortDescriptor2, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:#"state" cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor1 release];
[sortDescriptor2 release];
[sortDescriptors release];
}
return fetchedResultsController;
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
NSMutableArray *indices = [NSMutableArray array];
id <NSFetchedResultsSectionInfo> sectionInfo;
for( sectionInfo in [fetchedResultsController sections] )
{
[indices addObject:[sectionInfo name]];
}
return indices;
}
- (NSString *)tableView:(UITableView *)aTableView titleForHeaderInSection:(NSInteger)section
{
return [[[fetchedResultsController sections] objectAtIndex:section] name];
}
Try to implement in this way.
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
if ([[<#Fetched results controller#> sections] count] > 0) {
id <NSFetchedResultsSectionInfo> sectionInfo = [[<#Fetched results controller#> sections] objectAtIndex:section];
return [sectionInfo name];
} else
return nil;
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return [<#Fetched results controller#> sectionIndexTitles];
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
return [<#Fetched results controller#> sectionForSectionIndexTitle:title atIndex:index];
}
Related
DataModel.h code file
#import "DataModel.h"
#import <CoreData/CoreData.h>
#import "SettingsEntity.h"
#import "Constants.h"
#implementation DataModel
NSManagedObjectContext *managedObjectContextEntity;
NSManagedObjectContext *managedObjectContextMessage;
NSManagedObjectModel *managedObjectModel;
NSPersistentStoreCoordinator *persistentStoreCoordinator;
NSEntityDescription *theSettingsEntity;
NSEntityDescription *theMessagesEntity;
-(id) init
{
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"SOW" withExtension:#"momd"];
managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
NSError *error = nil;
NSURL *storeURL = [[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject] URLByAppendingPathComponent:[Constants SQLLiteDB]];
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:managedObjectModel];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
if (persistentStoreCoordinator != nil) {
managedObjectContextEntity = [[NSManagedObjectContext alloc] init];
[managedObjectContextEntity setPersistentStoreCoordinator:persistentStoreCoordinator];
managedObjectContextMessage = [[NSManagedObjectContext alloc] init];
[managedObjectContextMessage setPersistentStoreCoordinator:persistentStoreCoordinator];
}
theSettingsEntity = [NSEntityDescription entityForName:#"SettingsEntity" inManagedObjectContext:managedObjectContextEntity];
theMessagesEntity = [NSEntityDescription entityForName:#"MessageEntity" inManagedObjectContext:managedObjectContextMessage];
return self;
}
-(void) SaveSetting: (SettingsEntity *)setting
{
NSError *error = nil;
if (managedObjectContextEntity != nil) {
if ([managedObjectContextEntity hasChanges] && ![managedObjectContextEntity save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Confirmation" message:#"Settings were saved successfully." delegate:nil cancelButtonTitle:#"Okay" otherButtonTitles:nil, nil];
[alert show];
}
}
}
-(void) SaveMessage: (MessageEntity *)message
{
NSError *error = nil;
if (managedObjectContextMessage != nil) {
if ([managedObjectContextMessage hasChanges] && ![managedObjectContextMessage save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}
-(NSArray *) GetSettingsResult
{
NSManagedObjectContext *context = managedObjectContextEntity;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:theSettingsEntity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"enteredDateTime" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
NSArray *results = [context executeFetchRequest:request error:nil];
return results;
}
-(SettingsEntity *) GetSettingsEntity
{
SettingsEntity *settingsEntity = [NSEntityDescription
insertNewObjectForEntityForName:[theSettingsEntity name]
inManagedObjectContext:managedObjectContextEntity];
return settingsEntity;
}
-(NSArray *) GetMessageResult
{
NSFetchRequest *request = [self GetMessageFetchRequest];
NSArray *results = [managedObjectContextMessage executeFetchRequest:request error:nil];
return results;
}
-(NSFetchRequest *) GetMessageFetchRequest
{
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:theMessagesEntity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"sentDateTime" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
return request;
}
-(NSFetchedResultsController *) GetMessageFetchResultsController
{
NSFetchRequest *fetchRequest = [self GetMessageFetchRequest];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContextMessage sectionNameKeyPath:#"typeMessage" cacheName:nil];
return aFetchedResultsController;
}
-(MessageEntity *) GetMessageEntity
{
MessageEntity *mEntity = [NSEntityDescription
insertNewObjectForEntityForName:[theMessagesEntity name]
inManagedObjectContext:managedObjectContextMessage];
return mEntity;
}
#end
HistoryTableView code file is as follows:
#import "HistoryTableViewController.h"
#import "MessageEntity.h"
#import "DataModel.h"
#import "HistoryDetailViewController.h"
#import "Constants.h"
#interface HistoryTableViewController ()
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath;
#end
#implementation HistoryTableViewController
//todo: zulfiqar review this line of code.
#synthesize fetchedResultsController = __fetchedResultsController;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)awakeFromNib
{
[super awakeFromNib];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.rightBarButtonItem = self.editButtonItem;
//todo: get fetched controller here.. and reuse it every where in rest of this class.
}
/* faisal code starts here */
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSFetchedResultsController *c = [self fetchedResultsController];
NSArray *titles = [c sectionIndexTitles];
NSString *title = titles[section];
if ([title isEqualToString:#"E"])
{
return [Constants Email];
}
else if([title isEqualToString:#"T"])
{
return [Constants Text];
}
else
{
return [Constants Call];
}
}
- (NSArray *)sectionIndexTitlesForTableView: (UITableView *)aTableView
{
NSFetchedResultsController *c = [self fetchedResultsController];
return [c sectionIndexTitles];
}
-(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSFetchedResultsController *c = [self fetchedResultsController];
id<NSFetchedResultsSectionInfo> sectionInfo = c.sections[section];
return sectionInfo.numberOfObjects;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSFetchedResultsController *c = [self fetchedResultsController];
return c.sections.count;
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath *path = self.tableView.indexPathForSelectedRow;
HistoryDetailViewController *hdvc = segue.destinationViewController;
NSFetchedResultsController *c = [self fetchedResultsController];
MessageEntity *message = (MessageEntity *)[c objectAtIndexPath:path];
hdvc.currentMessageEntity = message;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark - Table view data source
/*
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSFetchedResultsController *c = [self fetchedResultsController];
id <NSFetchedResultsSectionInfo> sectionInfo = c.sections[section];
return sectionInfo.numberOfObjects;
}
*/
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSFetchedResultsController *c = [self fetchedResultsController];
NSString *CellIdentifier = [NSString stringWithFormat:#"Cell"];// %d_%d",indexPath.section,indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
MessageEntity *message = (MessageEntity *)[c objectAtIndexPath:indexPath];
cell.textLabel.text = message.typeMessage;
return cell;
/*
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"EEE. MMM. dd, yyyy HH:mm"];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
NSFetchedResultsController *c = [self fetchedResultsController];
MessageEntity *message = (MessageEntity *)[c objectAtIndexPath:indexPath];
NSString *sentDateTime = [dateFormat stringFromDate:message.sentDateTime];
NSString *contactAdd;
NSLog(message.typeMessage);
if ([message.typeMessage isEqualToString:[Constants Email]])
{
contactAdd = message.toEmailAddress;
}
else
{
NSString *unformatted = message.toPhoneNumber;
NSArray *stringComponents = [NSArray arrayWithObjects:[unformatted substringWithRange:NSMakeRange(0, 3)],
[unformatted substringWithRange:NSMakeRange(3, 3)],
[unformatted substringWithRange:NSMakeRange(6, [unformatted length]-6)], nil];
contactAdd = [NSString stringWithFormat:#"(%#) %#-%#", [stringComponents objectAtIndex:0], [stringComponents objectAtIndex:1], [stringComponents objectAtIndex:2]];
}
NSString *header = [[NSString alloc] initWithFormat:#"%#",contactAdd];
NSString *detail = [[NSString alloc] initWithFormat:#"%# on %#", message.sentName, sentDateTime];
cell.detailTextLabel.text = detail;
cell.textLabel.text = header;
return cell;
*/
}
// 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;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSFetchedResultsController *c = [self fetchedResultsController];
NSManagedObject *object = [c objectAtIndexPath:indexPath];
NSManagedObjectContext *context = self.fetchedResultsController.managedObjectContext;
[context deleteObject:object];
NSError *error = nil;
if (![context save:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
// The table view should not be re-orderable.
return NO;
}
- (void)tableView:(UITableView *)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView reloadData];
}
- (NSFetchedResultsController *)fetchedResultsController
{
if (__fetchedResultsController != nil) {
return __fetchedResultsController;
}
DataModel *data = [[DataModel alloc]init];
NSFetchedResultsController *aFetchedResultsController = [data GetMessageFetchResultsController];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
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 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 NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView endUpdates];
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
/*
<#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:#"<#Nib name#>" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
*/
}
#end
My history table view looks as follows:
**Q. Why Email and Call are getting into wrong section?**
If you set a sectionNameKeyPath for the fetched results controller, then you must also add a first sort descriptor to the fetch request that uses the same key (or a key that generates the same relative ordering).
So for sectionNameKeyPath:#"typeMessage" you should add one sort descriptor for the key "typeMessage":
NSSortDescriptor *sort1 = [[NSSortDescriptor alloc] initWithKey:#"typeMessage" ascending:YES];
NSSortDescriptor *sort2 = [[NSSortDescriptor alloc] initWithKey:#"sentDateTime" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sort1, sort2, nil];
[request setSortDescriptors:sortDescriptors];
Hi i'm fairly new to xcode and i'm having trouble getting my head around why im getting this error "use of undeclared identifier" I'm basically trying to return fetched results to a table view.
// UNBSearchBooksViewController.m
#import "UNBSearchBooksViewController.h"
#import "NBAppDelegate.h"
#interface UNBSearchBooksViewController ()
#end
#implementation UNBSearchBooksViewController
#synthesize booksSearchBar;
#synthesize searchTableView;
#synthesize fetchedResultsController, managedObjectContext;
- (void)viewDidLoad
{
[super viewDidLoad];
// NSFetchRequest needed by the fetchedResultsController
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// NSSortDescriptor tells defines how to sort the fetched results
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"title" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// fetchRequest needs to know what entity to fetch
NSEntityDescription *entity = [NSEntityDescription entityForName:#"BookInfo" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:#"Root"];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [fetchedObjects count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell" ;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell "this is where my problem is" use of undeclared identifier
BookInfo *info = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = info.title;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
}
- (void) searchBarSearchButtonClicked:(UISearchBar *)theSearchBar
{
if (self.booksSearchBar.text !=nil)
{
NSPredicate *predicate =[NSPredicate predicateWithFormat:#"bookName contains[cd] %#", self.booksSearchBar.text];
[fetchedResultsController.fetchRequest setPredicate:predicate];
}
else
{
NSPredicate *predicate =[NSPredicate predicateWithFormat:#"All"];
[fetchedResultsController.fetchRequest setPredicate:predicate];
}
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error])
{
// Handle error
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
fetchedObjects = fetchedResultsController.fetchedObjects;
[booksSearchBar resignFirstResponder];
[searchTableView reloadData];
}
- (void) searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
searchBar.showsCancelButton = YES;
}
- (void) searchBarTextDidEndEditing:(UISearchBar *)searchBar
{
searchBar.showsCancelButton = NO;
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
{
[searchBar resignFirstResponder];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
#end
Your problem is that you are assigning to a specific subclass the results typed as a base class. As you stated, this is where you get the warning:
BookInfo *info = [self.fetchedResultsController objectAtIndexPath:indexPath];
'NSFetchedResultsController' returns an object of type 'id' from
- (id)objectAtIndexPath:(NSIndexPath *)indexPath
(and I suppose it could return a NSManagedObject * too), but you assign it to a BookInfo object. Since there is a mismatch here, you need to cast the return value to what you know it is:
BookInfo *info = (BookInfo *)[self.fetchedResultsController objectAtIndexPath:indexPath];
and your warning will go away.
Using core data, data is being fetched properly and shown properly, issue is with the search that it does not filter the results, whatever I type in the search bar, it does show the same table view with same data in the filtered results..
- (void)viewDidLoad
{
[super viewDidLoad];
if (context == nil)
{
context = [(VektorAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}
app = [[UIApplication sharedApplication] delegate];
[self getData];
// create a filtered list that will contain products for the search results table.
filteredListItems = [[NSMutableArray alloc] initWithCapacity:[self.plateNumbers count]];
// restore search settings if they were saved in didReceiveMemoryWarning.
if (self.savedSearchTerm){
[self.searchDisplayController setActive:self.searchWasActive];
[self.searchDisplayController.searchBar setSelectedScopeButtonIndex:self.savedScopeButtonIndex];
[self.searchDisplayController.searchBar setText:savedSearchTerm];
self.savedSearchTerm = nil;
}
}
Fetching data from core data:
-(void)getData {
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Favouritesdata" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setFetchBatchSize:20];
[request setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"licenseplate" ascending:NO];
NSArray *newArray = [[NSArray alloc]initWithObjects:sort, nil];
[request setSortDescriptors:newArray];
NSLog(#"newArray: %#", newArray);
NSError *error;
results = [[context executeFetchRequest:request error:&error] mutableCopy];
plateNumbers = [results valueForKey:#"licenseplate"];
NSLog(#"plateNumbers: %#", plateNumbers);
[self setLicensePlateArray:results];
[self.favouritesTable reloadData];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == favouritesTable) {
return [licensePlateArray count];
} else { // handle search results table view
return [filteredListItems count];
}
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView == favouritesTable) {
cellValue = [licensePlateArray objectAtIndex:indexPath.row];
} else { // handle search results table view
cellValue = [filteredListItems objectAtIndex:indexPath.row];
}
static NSString *CellIdentifier = #"vlCell";
VehicleListCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSLog(#"Cell Created");
NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:#"VehicleListCell" owner:nil options:nil];
for (id currentObject in nibObjects) {
if ([currentObject isKindOfClass:[VehicleListCell class]]) {
cell = (VehicleListCell *)currentObject;
}
}
NSInteger cellVal = indexPath.row;
NSLog(#"indexPath.row: %i", cellVal);
UILongPressGestureRecognizer *pressRecongnizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(tableCellPressed:)];
pressRecongnizer.delegate = self;
pressRecongnizer.minimumPressDuration = 0.5f;
[cell addGestureRecognizer:pressRecongnizer];
[pressRecongnizer release];
}
cell.textLabel.font = [UIFont systemFontOfSize:10];
Favouritesdata *favdata = [results objectAtIndex:indexPath.row];
cell.licPlate.text = [favdata licenseplate];
NSLog(#"cellvalue for cellforRow: %#", cell.licPlate.text);
return cell;
}
Search bar implementation:
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:#"SELF contains[cd] %#",
searchText];
self.filteredListItems = [self.plateNumbers filteredArrayUsingPredicate:resultPredicate];
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
/* [self filterContentForSearchText:searchString scope:
[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];
*/
if ([[searchString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length])
self.favouritesTable = controller.searchResultsTableView;
return YES;
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption {
[self filterContentForSearchText:[self.searchDisplayController.searchBar text] scope:
[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:searchOption]];
return YES;
}
How to resolve this issue ?
Just make code clear before and define every methods, you are just setting predicate, not firing query for it.
just make separate method and just pass search text as parameter to that method for predicate and fire fetch query then reload table.
i generate a tableview using core-data and a NSFetchedResultsController with sectionNameKeyPath. My Core-Data entities look fine, also in the SQL-Database the Data looks good.
The Entity is called "Cast" and looks like this:
Cast
-> job
-> department // the attribute i want the sections from
i generate my NSFetchedResultsController like this
// fetch controller
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Cast" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort1 = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSSortDescriptor *sort2 = [[NSSortDescriptor alloc] initWithKey:#"job" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sort1, sort2, nil]];
[sort1 release];
[sort2 release];
// Predicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"movie == %#", self.movie];
[fetchRequest setPredicate:predicate];
// Generate it
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"department"
cacheName:nil];
self.fetchedResultsController = theFetchedResultsController;
self.fetchedResultsController.delegate = self;
[fetchRequest release];
[theFetchedResultsController release];
// Fetch Casts
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
// Update to handle the error appropriately.
XLog("Unresolved error %#, %#", error, [error userInfo]);
}
But the result is the following (i added the "department" attribute into the detail attribute to show the problem)
as you can see. the sections are generated properly, but then the single entities are completely random inserted into the sections.
can anybody see a bug in my code?
here is the rest of the code that is related to the cell/section stuff
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = nil;
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];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell...
Cast *currentCast = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = currentCast.name;
//cell.detailTextLabel.text = currentCast.job;
// just temporary
cell.detailTextLabel.text = currentCast.department;
return cell;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSString *jobTitle = [[[fetchedResultsController sections] objectAtIndex:section] name];
return jobTitle;
}
thanks for all hints.
please leave a comment if something is unclear.
You should sort by department first.
NSSortDescriptor *sort1 = [[NSSortDescriptor alloc] initWithKey:#"department" ascending:YES];
NSSortDescriptor *sort2 = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSSortDescriptor *sort2 = [[NSSortDescriptor alloc] initWithKey:#"job" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sort1, sort2, sort3, nil]];
[sort1 release];
[sort2 release];
[sort3 release];
I'm writing a small iPhone app for my company that shows bookings for each employee one week at a time. I'm using core data to get a list of 'Bookings' for a given week and want to display them in a UITableView broken down in to one section per day of the week.
The problem comes in that I need to show 7 sections for each day of the week (showing a 'No Bookings' cell where a section/date has no bookings).
I've got a screenshot of the app as it stands here (sorry can't post images yet as I'm new to StackOverlow)
At the moment I'm achieving this by using a 'fetchResults' method which gets the bookings and organises them in to an array of possible dates:
- (void)refetchResults {
// Drop bookings Array, replacing with new empty one
// 7 slots for 7 days each holding mutable array to recieve bookings where appropraite
self.bookings = [NSArray arrayWithObjects:[NSMutableArray array],
[NSMutableArray array], [NSMutableArray array],
[NSMutableArray array], [NSMutableArray array],
[NSMutableArray array], [NSMutableArray array], nil];
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Booking" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Limit to this weeks data
[fetchRequest setPredicate:
[NSPredicate predicateWithFormat:#"(date >= %#) && (date <= %#) && (resource == %#)",
firstDate,lastDate,resourceId]];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"date" ascending:YES];
NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:#"recId" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, sortDescriptor2, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Fetch records in to array
NSError *error;
NSArray *results = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (results == nil) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
[fetchRequest release];
[sortDescriptor release];
[sortDescriptor2 release];
[sortDescriptors release];
// Walk through records and place in bookings Array as required
for (Booking *item in results) {
// Decide on array index by difference in firstDate and booking date
int idx = (int)[[item date] timeIntervalSinceDate:firstDate]/86400;
// Add the item to the approp MutArray
[(NSMutableArray *)[bookings objectAtIndex:idx] addObject:item];
}
// Reload table
[tableView reloadData];
}
My question is: is there any way to achieve the same result using NSFetchedResultsController? Somehow I'd need to get the NSFetchedResultsController to have 7 sections, one for each day of the week, some of them possibly having no bookings.
Any help much appreciated :)
So, being as the weather isn't very nice outside I've had a go at answering my own question and implementing the 'workaround' described in my reply to westsider.
The idea is to hold a 'mapping' array (just a simple 7 slot int array) which will map the section the tableview will ask for to the underlying fetchedresultscontroller section. Each array slot will have the appropriate section index or '-1' where there are no underlying sections (and where a 'No Booking' cell should be shown instead).
So, my refetchResults method becomes:
- (void)refetchResults {
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Booking" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Limit to this weeks data
[fetchRequest setPredicate:
[NSPredicate predicateWithFormat:#"(date >= %#) && (date <= %#) && (resource == %#)",
firstDate,lastDate,resourceId]];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"date" ascending:YES];
NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:#"recId" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, sortDescriptor2, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Set up FRC
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"date" cacheName:nil];
self.fetchedResultsController = aFetchedResultsController;
self.fetchedResultsController.delegate = self;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptor2 release];
[sortDescriptors release];
// Run up FRC
NSError *error = nil;
if (![fetchedResultsController_ performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
// Update FRC map
[self updateFRCMap];
// Reload table
[tableView reloadData];
}
The mapping is set in the following method. This is called whenever the mapping needs to be refreshed - for example when I get callbacks from the fetchedresultscontroller for items that have been added/deleted/etc.
- (void)updateFRCMap {
// Set mapping table for seven days of week to appropriate section in frc
for (int idx=0;idx<7;idx++) { frcMap[idx] = -1; } // Reset mappings
// For each section
for (int sidx=0; sidx<[[self.fetchedResultsController sections] count]; sidx++)
{
// If section has items
if ([[[self.fetchedResultsController sections] objectAtIndex:sidx] numberOfObjects] > 0)
{
// Look at first booking of section to get date
NSDate *date = [(Booking *)[self.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:sidx]] date];
// Decide on array index by difference in firstDate and booking date
int idx = (int)[date timeIntervalSinceDate:firstDate]/86400;
// Set map
frcMap[idx] = sidx;
}
}
}
This can probably be optimised a bit but works OK for now. I suspect it might suffer GMT/BST clock change problems which will need fixing ... not that clock change problems are all that urgent, eh Apple? ;P
After that it's just a case of using the mapping array when responding to the tableview:
#pragma mark -
#pragma mark Table view data source
// Gets the booking from the fetchedResultsController using a remapped indexPath
- (Booking *)bookingForMappedIndexPath:(NSIndexPath *)indexPath {
return (Booking *)[self.fetchedResultsController objectAtIndexPath:
[NSIndexPath indexPathForRow:indexPath.row inSection:frcMap[indexPath.section]]];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 7; // 7 days viewed
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Rows in section or 1 if no section
if (frcMap[section] != -1) {
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:frcMap[section]];
return [sectionInfo numberOfObjects];
} else {
return 1;
}
}
- (UITableViewCell *)tableView:(UITableView *)_tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"RegularCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell.
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
// If no actual bookings for section then its a blank cell
if (frcMap[indexPath.section] == -1) {
// Configure a blank cell.
cell.textLabel.text = #"No Bookings";
cell.detailTextLabel.text = #"";
cell.textLabel.font = [UIFont systemFontOfSize:16];
cell.textLabel.textColor = [UIColor lightGrayColor];
cell.accessoryType = UITableViewCellAccessoryNone;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
} else {
// Regular cell
Booking *booking = [self bookingForMappedIndexPath:indexPath];
cell.textLabel.text = booking.desc;
cell.detailTextLabel.text = [NSString stringWithFormat:#"%# %#", booking.location, booking.detail];
cell.textLabel.font = [UIFont systemFontOfSize:14];
cell.textLabel.textColor = [UIColor darkTextColor];
cell.detailTextLabel.font = [UIFont systemFontOfSize:12];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
}
}
Any comments or better ways of writing this are very much welcome :)
I haven't used this much, but you might check out NSFetchedResultsSectionInfo protocol. It can be used like this, apparently:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSInteger numberOfRows = 0;
if ([[fetchedResultsController sections] count] > 0)
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
numberOfRows = [sectionInfo numberOfObjects];
}
return numberOfRows;
}
Good luck.
I had this problem too. I've written a subclass of NSFetchedResultsController to solve the issue:
https://github.com/timothyarmes/TAFetchedResultsController
Tim