UITableView cellForRowAtIndexPath causing slow scrolling - iphone

My table is scrolling incredibly slow, and I think it's caused by the Core Data methods within my cellForRowAtIndexPath. Below is my cellForRowAtIndexPath method:
- (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] autorelease];
}
NSManagedObject *info = [buildingArray objectAtIndex: [indexPath row]];
// all rooms have been scanned for building
if([self allRoomsScanned: [[info valueForKey:#"buildingid"] intValue]]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[cell.textLabel setTextColor: [UIColor lightGrayColor]];
}
else {
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
[cell.textLabel setTextColor: [UIColor blackColor]];
}
[cell.textLabel setFont:[UIFont fontWithName:#"Helvetica-Bold" size:16.0]];
[cell.textLabel setText:[info valueForKey:#"buildingname"]];
return cell;
}
And here is the allRoomsScanned method and allDevicesScanned method:
- (BOOL) allRoomsScanned: (int) buildingID {
NSMutableArray *scannedRoomArray = [[NSMutableArray alloc] init];
// Get all user_device
NSManagedObjectContext *context = [self managedObjectContext];
NSError *error;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"user_device" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
NSNumber *deviceid = [NSNumber numberWithInt: 0];
NSNumber *roomid = [NSNumber numberWithInt: 0];
//int lastRoomID = 0;
for (NSManagedObject *info in fetchedObjects) {
// Get all device
deviceid = [info valueForKey:#"deviceid"];
fetchRequest = [[NSFetchRequest alloc] init];
entity = [NSEntityDescription
entityForName:#"device" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(deviceid = %d)", [deviceid intValue]];
[fetchRequest setPredicate:predicate];
NSArray *fetchedDevices = [context executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *infod in fetchedDevices) {
// Get all room
roomid = [infod valueForKey:#"roomid"];
fetchRequest = [[NSFetchRequest alloc] init];
entity = [NSEntityDescription
entityForName:#"room" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(roomid = %d) AND (buildingid = %d)", [roomid intValue], buildingID];
[fetchRequest setPredicate:predicate];
NSMutableArray *fetchedRoom = [[context executeFetchRequest:fetchRequest error:&error] mutableCopy];
// add room to array if room belongs to selected building and room not already added
if([fetchedRoom count] > 0) { //&& lastRoomID != [roomid intValue]) {
for (NSManagedObject *info in fetchedRoom) {
NSLog(#"room id: %#", [info valueForKey:#"roomid"]);
// add room ids to array if not already there
if (![scannedRoomArray containsObject:[info valueForKey:#"roomid"]] && [self allDevicesScanned:[[info valueForKey:#"roomid"] intValue]])
[scannedRoomArray addObject: [info valueForKey:#"roomid"]];
}
//lastRoomID = [roomid intValue];
}
}
}
fetchRequest = [[NSFetchRequest alloc] init];
entity = [NSEntityDescription
entityForName:#"room" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(buildingid = %d)", buildingID];
[fetchRequest setPredicate:predicate];
NSArray *fetchedRoomTotal = [context executeFetchRequest:fetchRequest error:&error];
//NSLog(#"Total Rooms for Building: %d", [fetchedRoomTotal count]);
//NSLog(#"Scanned Rooms for Building: %d", [scannedRoomArray count]);
//NSLog(#"Scanned rooms: %#", scannedRoomArray);
if([fetchedRoomTotal count] == [scannedRoomArray count] && [fetchedRoomTotal count] > 0) {
return YES;
}
else {
return NO;
}
}
- (BOOL) allDevicesScanned: (int) roomID {
NSMutableArray *scannedDeviceArray = [[NSMutableArray alloc] init];
// Get all user_device
NSManagedObjectContext *context = [self managedObjectContext];
NSError *error;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"user_device" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
NSNumber *deviceid = [NSNumber numberWithInt: 0];
//NSNumber *roomid = [NSNumber numberWithInt: 0];
for (NSManagedObject *info in fetchedObjects) {
// Get all device
deviceid = [info valueForKey:#"deviceid"];
fetchRequest = [[NSFetchRequest alloc] init];
entity = [NSEntityDescription
entityForName:#"device" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(deviceid = %d) AND (roomid = %d)", [deviceid intValue], roomID];
[fetchRequest setPredicate:predicate];
NSArray *fetchedDevices = [context executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *infod in fetchedDevices) {
// add device to array
if([fetchedDevices count] > 0) {
NSLog(#"room id: %d", roomID);
// add device ids to array if not already there
if (![scannedDeviceArray containsObject:deviceid])
[scannedDeviceArray addObject: deviceid];
}
}
}
fetchRequest = [[NSFetchRequest alloc] init];
entity = [NSEntityDescription
entityForName:#"device" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(roomid = %d)", roomID];
[fetchRequest setPredicate:predicate];
NSArray *fetchedDeviceTotal = [context executeFetchRequest:fetchRequest error:&error];
//NSLog(#"Total Devices for Room: %d", [fetchedDeviceTotal count]);
//NSLog(#"Scanned Devices for Room: %d", [scannedDeviceArray count]);
//NSLog(#"Scanned Devices: %#", scannedDeviceArray);
if([fetchedDeviceTotal count] == [scannedDeviceArray count] && [fetchedDeviceTotal count] > 0) {
return YES;
}
else {
return NO;
}
}
Any idea on how to get rid of the latency when scrolling? I'm assuming I may be doing something inefficiently with either my core data calls or the way I'm calling the method in cellForRowAtIndexPath.
Thank for any help. It is greatly appreciated.

You really should not make Fetch Requests and processing like that on the main thread while scrolling a table view.
As rokjarc said, you should definitely save the result of your (quite heavy) allRoomsScanned method. I'd suggest adding a new style, i.e. with an activity indicator, that the cell gets when you don't have a result for that input yet. As soon as the load is complete you refresh the table view cell.
Attention: You can't use your default NSManagedObjectContext in allRoomsScanned and allDevicesScanned.
You need to initialize a new context in the background thread. Either initialize a new context at the beginning of the block and pass it as a method parameter or create a new one right in the methods.
NSManagedObject *info = [buildingArray objectAtIndex: [indexPath row]];
NSNumber *cachedResult = [self.scanResults objectForKey:info.objectID];
if (cachedResult == nil) {
// style loading state
int scanInfo = [[info valueForKey:#"buildingid"] intValue];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
BOOL result = [self allRoomsScanned: scanInfo];
[self.scanResults setObject:[NSNumber numberWithBool:result] forKey:info.objectID];
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]
});
} else if (cachedResult.boolValue == YES) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[cell.textLabel setTextColor: [UIColor lightGrayColor]];
} else if (cachedResult.boolValue == NO) {
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
[cell.textLabel setTextColor: [UIColor blackColor]];
}
[cell.textLabel setFont:[UIFont fontWithName:#"Helvetica-Bold" size:16.0]];
[cell.textLabel setText:[info valueForKey:#"buildingname"]];
return cell;

Related

Update value core data

I need to update the value of the desired cell in my core data. I have a cell (ID) with a known value to me. I need to find the value and replace. I know how to change the elements of the array. The following shows how I do it. But I have to change the field (ID) in MyBase. value that I need to change will be equal to fromIndexPath.row. The value on which I need to change toIndexPath.row.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath
*)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
if (fromIndexPath.section == toIndexPath.section) {
NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription* entityDescription = [NSEntityDescription entityForName:#"MyBase"
inManagedObjectContext:self.objectContext];
[fetchRequest setEntity:entityDescription];
empArray = [(NSArray*)[self.objectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
MyBase *employee = [empArray objectAtIndex:fromIndexPath.row];
[empArray removeObjectAtIndex:fromIndexPath.row];
[empArray insertObject:objectToMove atIndex:toIndexPath.row];
}
}
for update core data, you can try this:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#:#"Employee"
inManagedObjectContext:self.managedObjectContext];
[request setEntity:entity];
NSPredicate *pred = [NSPredicate predicateWithFormat:[NSString stringWithFormat:#"empId = %d", 12345]];
[request setPredicate:pred];
NSArray *empArray=[self.managedObjectContext executeFetchRequest:request error:nil];
[request release];
if ([empArray count] > 0){
Employee *employee = [empArray objectAtIndex:fromIndexPath.row];;
employee.empSalary=[NSNumber numberWithInt:45000];
employee.empName=#"John";
employee.empDesignation=#"Analysist";
employee.empExp=#"4 Years";
[self.managedObjectContext save:nil];

Table view and Predicates

I have a table view with 40 objects. I have to filter them by gender when Click in a UISegment (male, female and both). It seems to be working, but table view does not refresh. Please any help would be appreciatte.
-(void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.title = #"People";
[self loadall];
[[NSBundle mainBundle] loadNibNamed:#"FilterSortView" owner:self options:nil];
self.filterControl.selectedSegmentIndex = -1;
[self.filterControl addTarget:self action:#selector( changeSegge ) forControlEvents:UIControlEventValueChanged];
}
#
My second method to filter by gender
- (void)changeSegge
{
NSEntityDescription *personEntity = [NSEntityDescription entityForName:#"Person"
inManagedObjectContext:self.managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:personEntity];
NSError *error = nil;
self.people = [self.managedObjectContext executeFetchRequest:request error:&error];
if (error)
{
[NSException raise:NSInternalInconsistencyException format:#"Could not fetch Core Data records: %#",error];
}
if(filterControl.selectedSegmentIndex == 0){
NSPredicate *predicatem =[NSPredicate predicateWithFormat:#"gender == %#", #"m" ];
request.predicate=predicatem;
[self.tableView reloadData];
[request release];
NSLog(#"button 1");
}
Thanks a lot.
You forgot to fill people array after predicate set to fetch request.
Here is updated changeSegge method:
- (void)changeSegge
{
NSEntityDescription *personEntity = [NSEntityDescription entityForName:#"Person"
inManagedObjectContext:self.managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:personEntity];
NSError *error = nil;
self.people = [self.managedObjectContext executeFetchRequest:request error:&error];
if (error)
{
[NSException raise:NSInternalInconsistencyException format:#"Could not fetch Core Data >records: %#",error];
}
if(filterControl.selectedSegmentIndex == 0){
NSPredicate *predicatem =[NSPredicate predicateWithFormat:#"gender == %#", #"m" ];
request.predicate=predicatem;
self.people = [self.managedObjectContext executeFetchRequest:request error:&error];
[self.tableView reloadData];
[request release];
NSLog(#"button 1");
}

Function in NSManagedObjectClass is failing

I have a NSManagedObjectClass Favorites. In this class I have a method getFavoritesByArtistId You can see it over here.
-(Favorites *)getFavoriteById:(int)art_id{
Favorites *favorite;
NSLog(#"artist is is %d",art_id);
RKManagedObjectStore *store = [[GenkonStageDataModel sharedDataModel] objectStore];
NSManagedObjectContext *context = store.mainQueueManagedObjectContext;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Favorites"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"fav_art_id == %d",art_id];
[fetchRequest setPredicate:predicate];
NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:#"fav_id" ascending:YES];
fetchRequest.sortDescriptors = #[descriptor];
NSArray *matches = [context executeFetchRequest:fetchRequest error:nil];
if (matches.count > 0){
NSLog(#"till here");
favorite = [matches objectAtIndex:0];
}else {
NSLog(#"till here 2");
return NULL;
}
return favorite;
}
In my view controller I am calling it like this.
RKManagedObjectStore *store = [[GenkonStageDataModel sharedDataModel] objectStore];
NSManagedObjectContext *context = store.mainQueueManagedObjectContext;
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Favorites" inManagedObjectContext:context];
Favorites* favClass = [[Favorites alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:context];
Favorites *favorite = [favClass getFavoriteById:[artist.art_id intValue]];
Next I'm going to check
if(![favorite isKindOfClass:[NSNull class]]){
//Artist is already favorized !
}else{
//Artist is not favorized !
}
But my app always crashes with this CoreData _PFFastEntityRangesByType + 12, stop reason = EXC_BAD_ACCESS (code=1, address=0x38)
Any help?

Error when populating table view with coredata

I am working with coredata and successfully saved the data in data storage but when I try to output what I stored using table view ,I am not able to do it.
Here the user will enter and click save ..so when he goes back..I want the table to populated with what name he or she has entered.
So I wrote the below code for saving and find data:
- (void) saveData
{
coredata123AppDelegate *appDelegate =
[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context =
[appDelegate managedObjectContext];
Contacts *newContact;
newContact = [NSEntityDescription
insertNewObjectForEntityForName:#"Contacts"
inManagedObjectContext:context];
[newContact setValue:name.text forKey:#"name"];
[newContact setValue:address.text forKey:#"address"];
[newContact setValue:phone.text forKey:#"phone"];
name.text = #"";
address.text = #"";
phone.text = #"";
NSError *error;
[context save:&error];
status.text = #"Contact saved";
}
- (void) findContact
{
coredata123AppDelegate *appDelegate =
[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context =
[appDelegate managedObjectContext];
NSEntityDescription *entityDesc =
[NSEntityDescription entityForName:#"Contacts"
inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDesc];
NSPredicate *pred =
[NSPredicate predicateWithFormat:#"(name = %#)",
name.text];
[request setPredicate:pred];
NSManagedObject *matches = nil;
NSError *error;
NSArray *objects = [context executeFetchRequest:request
error:&error];
for(Contacts *info in objects)
{
NSLog(#"Name:%#",info.name);
NSLog(#"Address:%#",info.address);
}
if ([objects count] == 0) {
status.text = #"No matches";
} else {
matches = [objects objectAtIndex:0];
address.text = [matches valueForKey:#"address"];
phone.text = [matches valueForKey:#"phone"];
status.text = [NSString stringWithFormat:
#"%d matches found", [objects count]];
}
[request release];
}
And to populate the table view i have written this code..
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Contacts" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *error;
self.contacts = [context executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
}
- (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] autorelease];
}
Contacts *info =[contacts objectAtIndex:indexPath.row];
cell.textLabel.text =info.name;
cell.detailTextLabel.text=info.address;
// Configure the cell...
return cell;
}
Are you getting proper number of elements in this objects array?
NSArray *objects = [context executeFetchRequest:request
error:&error];
if not, try changing the predicate with,
[NSPredicate predicateWithFormat:#"name=%#", name.text];
Otherwise, your code seems correct to me.

Adding extra sections to a NSFetchedResultsController

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