Show sections with core data - ios

I want to code a todo app and display 2 sections. 1 for entrys, that have to be done and 1 for already done things.
I want something like that:
Things not done yet
Todo 1
Todo 2
Things already done
Todo 3
Todo 4
At the moment I have 1 section for the undone todos.
My code for the table view at the moment looks like this
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSArray *array = [[NSArray alloc] initWithObjects:#"To do",#"done",nil];
return [array objectAtIndex:section];
}
- (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];
}
And my code for the fetchedresultscontroller looks like this:
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSManagedObjectContext* context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription* entity = [NSEntityDescription entityForName:#"Todo" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"timestamp" ascending:YES];
NSArray *sortDescriptors = #[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"done == %#", #(NO)];
[fetchRequest setPredicate:predicate];
// 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.managedObjectContext sectionNameKeyPath:#"done" cacheName:nil];
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;
}
Objects with the key "NO" are not done and with the key "YES" are done. But I don't know how to display both values in different sections.
Edit
I found the solution to my problem…
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"done" cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
That solved this problem. But now sometimes an undone item shows up at done.
Or if there are two done items they appear under "Todo"
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSArray *array = [[NSArray alloc] initWithObjects:#"Todo",#"Done",nil];
return [array objectAtIndex:section];
}
- (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
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
Thats my actual code for that…
Now it looks like that. Undone todo unchecked, done todo checked. http://img.xnmn.de/i/2353ad.png
But if I check undone todo as done, they both appear at "todo" http://img.xnmn.de/i/5edff4.png

You're code looks almost right. Since you set the sectionKeyPath to "done", the fetched results controller will automatically create the sections for you. You could add another sort descriptor for the "done" attribute to sort the entries according to their section.
What you have to do is to remove the NSPredicate. In you current code, you're filtering out all entries with "done == YES", but since you have your two sections, you want the results controller to return all entries.
So basically you just need to remove the
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"done == %#", #(NO)];
[fetchRequest setPredicate:predicate];
Of course you'd also have to implement the – tableView:cellForRowAtIndexPath: method

If i get your Code right you already created the section which is fine.
- (UITableViewCell *)tableView:(UITableView *)inTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = #"Entry";
UITableViewCell *cell = [inTableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier];
}
YourObject *cellEntry = [_fetchedResultsController objectAtIndexPath:indexPath];
//init cell
}
This should choose the right entry for the right section otherwise the line where you set the section is wrong.
The index path chooses the right section.

Related

Core Data family members grouped by surname

This is my first attempt on Core Data so I would like your guidance.
My example project is quite simple, I would like to create an iOS app that displays a list of people. What I am looking for is to group all members of the same family together. Some people in the list do not belong to a "family". So, UITableView is going to be a mixture of groups and rows.
Here is my Model so far.
and here I am adding data in the context
Family *newFamily = [NSEntityDescription insertNewObjectForEntityForName:#“Family” inManagedObjectContext:self.managedObjectContext];
[newFamily setSurname:[dict objectForKey:#“surname”]];
NSMutableSet *members = [newTeam mutableSetValueForKey:#"members"];
for (NSDictionary *member in [dict objectForKey:#"members"]) {
Member *newMember = [NSEntityDescription insertNewObjectForEntityForName:#"Member" inManagedObjectContext:self.managedObjectContext];
[newMember setFirstName:[member objectForKey:#"firstName"]];
[newMember setAge:[member objectForKey:#“age”]];
[members addObject:newMember];
}
//Save context ...
In UITableViewController I am able to read and display just families'/groups' name. I am confused on how do you display all the objects of a section.
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Family" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"surname" ascending:NO];
[fetchRequest setSortDescriptors:#[sortDescriptor]];
// 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.managedObjectContext sectionNameKeyPath:#"surname" cacheName:#"Master"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
return _fetchedResultsController;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// I DON'T KNOW WHAT TO DO HERE ...
// return 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];
return [sectionInfo name];
}
First, you need to amend your model to include the inverse relationship, from Member to Family, which presumably is "to-one". Name that relationship "family". (This inverse relationship is needed in this case, see below, but you should almost always include inverses for all relationships, regardless of whether you think you need them.)
Next, amend your FRC configuration as follows: the underlying entity should be Member, since each row of your tableView will represent a Member object. The sectionNameKeyPath should be "family.surname" so the FRC will automatically put the Member objects into sections based on the surname attribute of the related Family object. It is important that the sort order for the FRC matches up with the sections (ie all Member objects in the same section must be sorted together), so amend the sort descriptors to use family.surname (you could then sort by firstName if desired):
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Member" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *surnameSortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"family.surname" ascending:NO];
NSSortDescriptor *firstNameSortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"firstName" ascending:NO];
[fetchRequest setSortDescriptors:#[surnameSortDescriptor, firstNameSortDescriptor]];
// 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.managedObjectContext sectionNameKeyPath:#"family.surname" cacheName:#"Master"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
return _fetchedResultsController;
}
Finally, there is boilerplate code for linking an FRC to a tableView (see the Apple Docs here). The piece you are missing is:
- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section {
if ([[self.fetchedResultsController sections] count] > 0) {
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
} else
return 0;
}
Your configureCell method will likewise need to determine the correct Member object to display using
Member *currentMember = [self.fetchedResultsController objectAtIndexPath:indexPath];
Note that the FRC will log a warning message if there are Member objects that are not in a Family, since the sectionName will be nil. But it will group these all together - you might want to amend the titleForHeaderInSection to use a different name for that section.
Also, note that the FRC's sections are determined by inspecting the family.surname for each Member object. If there are Family objects that have no members, those Family objects will not appear in the table view (i.e. no empty sections).

How to synchronize UITableView scrolling and CoraData fetch on iOS app?

I want to implement the following function, can you any help me out.
I have core data base in my app. In that data base one model CourseEvents contain more then 150000 records, and each record having around 12 fields.
Each records value for one UITableViewCell.
But i don't want to fetch all the records in a single fetch request. want to fetch the some N number of records according to the UITableView scroll.
Example:
When table view load first time want to fetch 200 records, whenever user scroll the UITableView need to fetch next 200 record, like need to fetch the data from model based on the scrolling of the UITableview.
How can i achieve this. kindly help.....
If I understand your question correctly, When you load the view initially you only want to fetch 200 records and on tableView Scroll you want to fetch next 200 and so on. You are using core data then it's easier to fetch records with the help of NSFetchedResultsController. Just set the setFetchBatchSize to whatever records you want to fetch (20 should be good in your case also). There are so many examples available online and great apple samples also available. Here is the link of CoreDataBooks example. This is great tutorial on how to use NSFetchedResultsController. And Finally Core Data Programming guide is there for your help. here is the sample code on how to use NSFetchedResultController
- (void)viewDidLoad {
[super viewDidLoad];
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
// Update to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
}
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"FailedBankInfo" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc]
initWithKey:#"details.closeDate" ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:managedObjectContext sectionNameKeyPath:nil
cacheName:#"Root"];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
id sectionInfo =
[[_fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
EntityName *entity = [_fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = entity.name; //just example
cell.detailTextLabel.text = [NSString stringWithFormat:#"%#, %#",
entity.city, entity.state];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Set up the cell...
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
This sample project helped me a lot to implement my solution. It may help you too. https://github.com/lukagabric/LargeDatasetSample

TableView Sections sort by expires next

so I'm working on a litte ToDo app.
The TableView shows a CoreData Entity with the attributes of the name(string) and the Date(Date).
Currently the NSfetchedResultsController sort the tableView by the Date of the ToDo, but i also want some sections for example "expired ToDo's - which have a date in the past" or "ToDo's for the next week"
how can I achieve this?
Code NSFetchedResultsController:
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
// Create and configure a fetch request with the Book entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Inventory" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Create the sort descriptors array.
NSSortDescriptor *productDescriptor = [[NSSortDescriptor alloc] initWithKey:#"inventoryProductName" ascending:YES];
NSSortDescriptor *dateDescriptor = [[NSSortDescriptor alloc] initWithKey:#"expireDate" ascending:YES];
NSArray *sortDescriptors = #[dateDescriptor,productDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
// Create and initialize the fetch results controller.
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"inventoryProductName" cacheName:#"Root"];
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
Code TableView:
- (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];
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
Inventory *inventory = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = inventory.inventoryProductName;
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateStyle:NSDateFormatterShortStyle];
NSDate *dt = inventory.expireDate;
NSString *dateAsString = [formatter stringFromDate:dt];
//[formatter release];
cell.detailTextLabel.text = [NSString stringWithFormat:#"Expires at: %#", dateAsString];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell.
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
You need to give your todo items another property that reflects what you want. As for "past", "next week" you could use a transient property that is calculated on the fly via a custom getter. In some cases, you will have to actually persist this as a (non-transient) attribute for your fetched results controller to work.
For predictable sorting, the actual attribute value could just be a number - this would allow you to also include date-independent categories, such as "canceled", "invalid", etc.

Core-Data NSFetchedResultsController to-many relationship

Here is my Datamodel:
I filled the Database with Data.
Now I want to display this in a tableview.
First I want to select a day with the 'days' attribute, then get its lessons.
I want to display the attributes 'end' and 'start' in the header (-(NSString *) tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section)
Then I want to display the lesson data in the right section. How can I do that with NSFetchedResultsController?
I've set up a basic code:
NSFetchRequest* fetch = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([Lessons class])];
NSSortDescriptor* sortGroup = [NSSortDescriptor sortDescriptorWithKey:#"lesson" ascending:YES];
fetch.sortDescriptors = #[sortGroup];
NSFetchedResultsController *controller = [[NSFetchedResultsController alloc] initWithFetchRequest:fetch managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
NSError* error;
[controller performFetch:&error];
if (error) {
NSLog(#"Error: %#", error);
abort();
}
EDIT:
Here is the Storyboard:
UPDATE
Here's my Code:
TableView.m (important Methods)
-(id)init
{
self = [super init];
if (self) {
NSLog(#"%s",__PRETTY_FUNCTION__);
self.del = [[UIApplication sharedApplication] delegate];
self.managedObjectContext = self.del.managedObjectContext;
NSFetchRequest* fetch = [NSFetchRequest fetchRequestWithEntityName:#"Lessons"];
NSString *theSelectedDay = #"Mi";
NSPredicate *pred = [NSPredicate predicateWithFormat:#"lessonToDay.day == %#", theSelectedDay];
fetch.predicate = pred;
NSSortDescriptor *sortTime = [NSSortDescriptor sortDescriptorWithKey:#"lessonToTime.start" ascending:YES];
//NSSortDescriptor *sortGroup = [NSSortDescriptor sortDescriptorWithKey:#"lesson" ascending:YES];
fetch.sortDescriptors = #[sortTime];
self.controller = [[NSFetchedResultsController alloc] initWithFetchRequest:fetch
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"lessonToTime.start"
cacheName:nil];
}
return self;
}
#pragma mark - Table view data source
- (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 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
// Configure the cell...
//UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
Lessons *lesson = [self.controller objectAtIndexPath:indexPath];
cell.textLabel.text = lesson.lesson;
return cell;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.controller sections] objectAtIndex:section];
// This is the first lesson in this section:
Lessons *lesson = [sectionInfo objects][0];
//For testing I changed the type of 'start' and 'end' to string
NSLog(#"start: %#",lesson.lessonToTime.start);
NSLog(#"end: %#", lesson.lessonToTime.end );
NSString *title = [NSString stringWithFormat:#"%#-%#",
lesson.lessonToTime.start,
lesson.lessonToTime.end];
return title;
}
RESULT:
First of all you have to add a predicate that only the lessons for the selected day are displayed:
NSPredicate *pred = [NSPredicate predicateWithFormat:#"lessonToDay.day == %#", theSelectedDay];
fetch.predicate = pred;
To group the table view into sections, you have to set the sectionNameKeyPath: parameter
of the fetched results controller, for example to #"lessonToTime.start". This would group
all lessons with the same start time into one section.
(As I understand your comment, lessons with the same start time have also the same end time,
so this should be sufficient.)
The same key path must be used as first sort descriptor:
NSSortDescriptor *sortTime = [NSSortDescriptor sortDescriptorWithKey:#"lessonToTime.start" ascending:YES];
NSSortDescriptor *sortGroup = [NSSortDescriptor sortDescriptorWithKey:#"lesson" ascending:YES];
fetch.sortDescriptors = #[sortTime, sortGroup];
NSFetchedResultsController *controller = [[NSFetchedResultsController alloc] initWithFetchRequest:fetch
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"lessonToTime.start"
cacheName:nil];
To display the section header according to your needs, you have to override titleForHeaderInSection::
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.controller sections] objectAtIndex:section];
// This is the first lesson in this section:
Lesson *lesson = [sectionInfo objects][0];
// Now build some title from lesson.lessonToTime.start and lesson.lessonToTime.end:
NSString *title = ...;
return title;
}
You can do this with two View Controllers:
DaysViewController - here you show all days. When user selects the cell you get the day associated with the cell and pass it to the second View Controller,
LessonsViewController - here you use the passed object when building your NSFetchRequest's predicate
UPDATE: my answer isn't correct anymore, because the problem's description have changed.

iOS UITableView sections with fetchedResultsController confusion

I have an entity being displayed in a table view in just one section. The entity has two attributes, workoutName and trainingLevel. Both are of string type. Training level consists of the 3 types: 1, 2, 3. (trainingLevel = (Integer 16 or String Type? Which would be ideal?) I would like to split the table into three sections, each section containing entries for the corresponding training level.
How do I do this? The code I am currently using is below:
- (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 self.workoutType.workouts.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
WorkoutSet *workoutSet = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = workoutSet.workoutName;
cell.detailTextLabel.text = [NSString stringWithFormat:#"(%d)", workoutSet.days.count];
}
-(void)fetchWorkoutSets
{
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"WorkoutSet"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"workoutType = %#", self.workoutType];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"workoutName" ascending:YES];
[fetchRequest setSortDescriptors:#[sortDescriptor]];
[fetchRequest setPredicate:predicate];
self.fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil cacheName:nil];
NSError *error;
if (![self.fetchedResultsController performFetch:&error])
{
NSLog(#"Fetch failed: %#", error);
}
}
What I am struggling with is:
How to determine the number of rows for each section through the core data model by fetching number of entries with training level 1 or 2 or 3.
How to fill the rows of each section by fetching the correct items.
How to give a title to each section header.
Here is a good tutorial on using fetchedResultsControllers: http://www.raywenderlich.com/999/core-data-tutorial-for-ios-how-to-use-nsfetchedresultscontroller
Create some properties to hold your context and fetches:
#property (nonatomic,strong)NSManagedObjectContext* managedObjectContext;
#property (nonatomic,retain)NSFetchedResultsController *fetchedResultsController;
In your fetchedResultsController property, use sectionKeyNamePath to set up your fetched results in sections:
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Workouts"
inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc]
initWithKey:#"workoutName" ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:managedObjectContext
sectionNameKeyPath:#"trainingLevel"
cacheName:#"Root"];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
Your initial population of your fetchedResultsController can happen in your -viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
// Update to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
}
You'd then return the number of sections and number of rows in sections like this:
- (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];
}
You can then get your managed object for the particular row like this:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// init the cell
// and whatever other setup needed
WorkoutSet *workoutSet =
[self.fetchedResultsController objectAtIndexPath:indexPath];
// configure the cell from the managedObject properties
}

Resources