Reputation: 5259
I am using core data and I am having a UITableView with dynamic number of sections.
I have an entity called dates - and it has let's say a title and a relationship which points to another entity - the id of that entity is the section data will be presented.
Which of the best is the best approach and why?
A. Have an array of NSFetchedResultControllers - in each section I filter the data using a predicate. Then I just present the data to each section.
B. I have a single NSFetchedResultController and I fetch all the data - then inside my CellForRow I check whether I should present them or not.
C. I remove the relationship and I add an extra attribute called sectionId in my entity and use either A or B.
What is best approach in terms of UI performance?
EDIT :
Example - I have
Entity 1 : Data
Data Id : 0, Title : First, (Relationship) section : 0
Data Id : 1, Title : Second, (Relationship) section : 0
Data Id : 2, Title : FisrtB, (Relationship) section : 1
Entiry 2 : SectionsName
SectionId : 0 , Name : TitleA , etc (to-many- relationhsip to Data)
SectionId : 1 , Name : TitleB , etc (to-many- relationhsip to Data)
So, question is actually :
indexPath.row
, the same for the second
FRC.Upvotes: 0
Views: 65
Reputation: 21536
Edited to reflect changes to original question
You almost certainly should use Option B from your original question (Option A in your subsequent edit): a single NSFetchedResultsController
, based on the Data
entity but with table view sections determined by the section
relationship.
Your fetched results controller can do all the hard work of dividing the objects up into the correct sections: ensure that the fetch underlying the FRC is sorted first by section.sectionId
(or section.name
if you prefer), and specify the FRC's sectionNameKeyPath
as section.sectionId
(or section.name
). The boilerplate FRC/tableView code will then automatically put objects into the correct sections.
The aforementioned boilerplate code:
#pragma mark - TableView Datasource delegate
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return self.fetchedResultsController.sections.count;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = self.fetchedResultsController.sections[section];
return [sectionInfo numberOfObjects];
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
Data *data = [self.fetchedResultsController objectAtIndexPath:indexPath];
....
return cell;
}
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = self.fetchedResultsController.sections[section];
return sectionInfo.name;
}
#pragma mark - Fetched results controller
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Data" inManagedObjectContext:self.context];
[fetchRequest setEntity:entity];
// Edit the sort key as appropriate.
NSSortDescriptor *sectionSort = [[NSSortDescriptor alloc] initWithKey:@"section.sectionId" ascending:YES];
// Add any other sort criteria
....
NSArray *sortDescriptors = @[sectionSort, ...];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.context sectionNameKeyPath:@"section.sectionId" cacheName:nil];
self.fetchedResultsController = aFetchedResultsController;
aFetchedResultsController.delegate = self;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
return _fetchedResultsController;
}
#pragma mark - FRC delegate methods
- (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;
default:
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:
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeMove:
[tableView moveRowAtIndexPath:indexPath toIndexPath:newIndexPath];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView endUpdates];
}
Upvotes: 1
Reputation: 80265
It is almost certainly better to use only one fetched results controller. (I agree with @Lee on this point, but I do not understand why he is recommending option B, which includes more FRCs.)
To summarize your data model:
Section <------->> Date
You can simply fetch the section and just adjust the datasource
methods:
// number of sections
return self.fetchedResultsController.fetchedObjects.count;
// number of rows in section
Section *section = [self.fetchedResultsController objectAtIndexPath:
[NSIndexPath indexPathForRow:section inSection:0]];
return section.dates.count;
// cellForRowAtIndexPath
/* create a convenience function in the Section class to return a
sorted array from the NSSet "dates". */
Section *section = [self.fetchedResultsController objectAtIndexPath:
[NSIndexPath indexPathForRow:indexPath.section inSection:0]];
Date *date = section.sortedDates[indexPath.row];
/* configure the cell based on the date object */
So, A
is the better option.
Upvotes: 1
Reputation: 117
It seems that though you only need one attribute from the entity 2 (sectionID), you still need the relationship in order to get that entity and eventually get that attribute (sectionID). Therefore I suggest option B. The less FRCs the better. You will only need one here. In your CellForRow you will get the current entity in your fetched results array by using the indexPath. Once you have the current entity you can then now access entity 2, which is a property of the first entity. Once you have entity 2, you now have the attribute you've wanted, which is the section id of entity 2. You can now display that attribute (section id) on your table view.
Upvotes: 1