I have a app that works with Core Data. The data has a field with a date and I would like to show every entry of a month in a seperate section.
How do I actually get the data ? I use a NSFetchedResultsController to get the data and use this :
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[_fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
to get the rows and this :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MyInfoObject *info = [_fetchedResultsController objectAtIndexPath:indexPath];
}
to get my actually data object.
Thanks
Make sure you set the sectionNameKeyPath when instantiating your fetchedResultsController
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:#"dateKey" cacheName:#"Root"];
Related
I have data on a server which I want to show in the a tableView.
The problem is that I want to show data based on categories so I have array categories which has categories which will be section titles and inside them there data so for display the data in section I have to declare Array.
e.g. If there are three categories then we have to make three array to populate data but what if there are more categories as categories are dynamic and come from server.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [categoryArray count] ;
}
And how to set title for section title, as it is in category array, so if it is section one by one in array.
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSLog(#"Number of Sections");
if(section == 0)
return #"Sales";
if(section == 1)
return #"Soft Skills";
}
How to show data in tableView cells may I create arrays for all the categories?
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (section==0)
{
appDelegate = (MultipleDetailViewsWithNavigatorAppDelegate *)[[UIApplication sharedApplication] delegate];
int count=[resultArray count];
NSLog(#"resultArry Row Counts is %d",count);
return [resultArray count];
}
else{
appDelegate = (MultipleDetailViewsWithNavigatorAppDelegate *)[[UIApplication sharedApplication] delegate];
int count=[resultArrayOne count];
NSLog(#"resultArry Row Counts is %d",count);
return [resultArrayOne count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"Table Cell Data");
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
if (indexPath.section==0) {
appDelegate = (MultipleDetailViewsWithNavigatorAppDelegate *)[[UIApplication sharedApplication] delegate];
ObjectData *theCellData = [resultArray objectAtIndex:indexPath.row];
NSString *cellValue =theCellData.sub_Category;
cell.font=[UIFont fontWithName:#"Helvetical Bold" size:14];
NSLog(#"Cell Values %#",cellValue);
cell.textLabel.text = cellValue;
return cell;
}
else {
appDelegate = (MultipleDetailViewsWithNavigatorAppDelegate *)[[UIApplication sharedApplication] delegate];
ObjectData *theCellData = [resultArrayOne objectAtIndex:indexPath.row];
NSString *cellValue =theCellData.sub_Category;
cell.font=[UIFont fontWithName:#"Helvetical Bold" size:14];
NSLog(#"Cell Values %#",cellValue);
cell.textLabel.text = cellValue;
return cell;
}
}
Since getting the categories from the server does not seem to be your question I base my answer on pre filled arrays for a better visualization.
NSMutableArray *categories = #[#"Cat1", #"Cat2"];
// creata a dictionary with all the array for the categorie rows
NSMutableDictionary *rowDict = #{#"Cat1":#[#"Cat1Row1",#"Cat1Row2",#"..."],
#"Cat2":#[#"Cat2Row1", #"Cat2Row2",#"..."]
Key to this solution is that you use the category string as key for the dictionary.
You can now access the title like this
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return categories[section];
}
And access your rows like this
- (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// ...
// create or deque a cell like you normally would do
// now configure the cell
cell.textLable.text = [rowDict[categories[indexPath.section]] objectAtIndex:indexPath.row]
}
use
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [categoryArray objectAtindex:section];
}
for section title.
likewise, store the values for each categories in a nsmutable array NSDictionary and display the data for each category in uitableviewcell.
Try the below code. It'll solve your problem :
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [categoryArray objectAtIndex:section];
}
EDIT :
First Create a model data class to store data of your categories.
Use this model class to feel your numberOfRowsInSection and cellForRowAtIndexPath delegate function.
Instead of creating different-different array for each category. Store this array in one model class. It'll be easy to handle.
I have a NSMutableArray filled with NSMutableArray's. I would like to fill my table view up with the appropriate amount of rows, based on the size of a particular indexes array.
I currently have the array setup to grab the first element in the array, and the table then sets the amount of rows to that particular array's size. Ideally, i'd like to set the rows to the count of each element, of which most (arrays) have differing sizes.
Here is what I currently have:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
WorkoutManager *workoutManager = [WorkoutManager sharedInstance];
NSMutableArray *blah = [[workoutManager workouts] objectAtIndex:0];
return [blah count];
}
Any help would be greatly appreciated.
Do it in following way:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
WorkoutManager *workoutManager = [WorkoutManager sharedInstance];
return [[workoutManager workouts] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
WorkoutManager *workoutManager = [WorkoutManager sharedInstance];
NSMutableArray *blah = [[workoutManager workouts] objectAtIndex:indexPath.section];
return [blah count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
WorkoutManager *workoutManager = [WorkoutManager sharedInstance];
NSMutableArray *blah = [[workoutManager workouts] objectAtIndex:indexPath.section];
YourObj *obj = [blah objectAtIndex:indexPath.row];
//Do further...
}
Im working with the CoreDataBooks example project from Apple and I want to have a custom cell at indexPath 0 in my tableview and then have my core data fetched results start from index 1.
I have tried some different solutions but can't get it working, if you have any ideas it would due appreciated thanks.
If you want to know what I have tried and failed let me know. What I want to achieve seems simple, just start the fetched results at cell 1 rather than 0.
Edit 2:
All my UITableViewDataSource and configureGuestCell code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == 0)
{
static NSString *CellIdentifier = #"statsCell";
GuestStatsCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[GuestStatsCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
//Configure the cell.
[self configureStatsCell:cell];
return cell;
}
if (indexPath.row > 0)
{
static NSString *CellIdentifier = #"guestCell";
customGuestCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[customGuestCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell.
[self configureGuestCell:cell atIndexPath:indexPath];
return cell;
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
int cellHeight;
if (indexPath.row == 0)
{
cellHeight = 240;
}
else
{
cellHeight = 44;
}
return cellHeight;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[fetchedResultsController sections] count];
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (void)configureGuestCell:(customGuestCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
//Configure the cell to show the Guests first and last name and other details
GuestInfo *guest = [fetchedResultsController objectAtIndexPath:indexPath];
cell.guestNameLbl.text = [NSString stringWithFormat:#"%# %#", guest.firstName, guest.lastName];
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
// Delete the managed object.
NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];
[context deleteObject:[fetchedResultsController objectAtIndexPath:indexPath]];
NSError *error;
if (![context save:&error])
{
// Update to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Create and push a detail view controller.
guestListDetailViewController *detailViewController = [[guestListDetailViewController alloc] initWithStyle:UITableViewStyleGrouped];
GuestInfo *selectedGuest = (GuestInfo *)[[self fetchedResultsController] objectAtIndexPath:indexPath];
// Pass the selected book to the new view controller.
detailViewController.guest = selectedGuest;
[self.navigationController pushViewController:detailViewController animated:YES];
}
Fixed code:
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects] +1;
}
- (void)configureGuestCell:(customGuestCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
NSIndexPath *path = [NSIndexPath indexPathForRow:(indexPath.row - 1) inSection:indexPath.section]
//Configure the cell to show the Guests first and last name and other details
GuestInfo *guest = [fetchedResultsController objectAtIndexPath:path];
cell.guestNameLbl.text = [NSString stringWithFormat:#"%# %#", guest.firstName, guest.lastName];
}
Small explanation:
you should return a one row more in numberOfRowsInSection, but, to prevent errors, row number should be decremented before calling [NSFetchedResultsController objectAtIndexPath].
Add 1 to the indexPath.row value....?!
For this to work you also need to edit the -tableView: numberOfRowsInSection: to return (realRows + 1) as well.
Likewise you will want to decrement the row by one whenever you are referencing your NSFetchedResultsController | NSArray which I suspect is in your configureCell:atIndexPath:
Anywhere you want/need to touch the data source you will need to adjust for that row either by incrementing or decrementing.
Response to OP
Don't change the NSIndexPath, there is no need. Adjust the index in your -configureCell: atIndexPath: or anywhere else that touches that index value:
-(void)configureCell:(id)cell atIndexPath:(NSIndexPath*)indexPath
{
NSInteger finalIndex = [indexPath row] - 1;
NSIndexPath *newPath = [NSIndexPath indexPathForRow:finalIndex inSection:[indexPath section]];
id object = [[self myNSFRC] objectAtIndexPath:newPath];
//Continue configuring your cell
}
Obviously if you are using an array then don't bother creating a new NSIndexPath.
I am working on a todo list application with CoreData + UITableView, I would like to hide the row that the user mark as done.
My current solution is invoke deleteRowsAtIndexPaths when user mark the task done and deduct the deleted row from the function of numberOfRowsInSection.
-(void)markDone:(NSIndexPath *) _indexPath{
[self.tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:_indexPath] withRowAnimation:UITableViewRowAnimationFade];
deletedCount = deletedCount + 1;
[self.tableView endUpdates];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
if (deletedCount>0) {
return [sectionInfo numberOfObjects]-deletedCount;
}
return [sectionInfo numberOfObjects];
}
Although this method does work, but I do need some code hacking here and there. Is there a way to invoke NSFetchedResultsController didChangeObject for changing of status of particular field?
Thanks
I think there are many ways to solve this. I'd just add a field in your managed object which states if a row is hidden or not"
Deleting will set this field accordingly.
What you need now is an NSFetchRequest with the corresponding predicate to filter hidden rows.
I just created a simple template app with core data support, and I think it is very easy to achieve:
I added a hidden BOOL property to the one entitiy given, with default NO.
Then I added this code to didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSManagedObject *selectedObject = [[self fetchedResultsController] objectAtIndexPath:indexPath];
[selectedObject setValue:[NSNumber numberWithBool:YES] forKey:#"hidden"];
}
In - (NSFetchedResultsController *)fetchedResultsController {... I added
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"hidden == NO"];
[fetchRequest setPredicate:predicate];
after
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
This was it to hide cells by clicking(just for this example) on them.
I have two tableviews where one navigates to the next. Both use more or less the same code and use the managedObjectContext and FetchedResultsController design pattern to provide the relevant data for both tables.
My problem happens when I navigate from the first to the second tableview and when the datasource has only one Section Header / Row to display in the tableview. I'm finding the datasource does not update Section Header information. It will however update this information if there is more than one row or section data passed the second tableview.
This issue is only really noticeable when attempting to navigate the same criteria of data from a different row of the first tableview a second time. Somehow it has not released the information from the first attempt and instead has used this information as a placeholder. I can confirm memory management methods have been done and that the corresponding Row below the Section Header is using the correct data so my managedObjectModel is being passed correctly.
Has anyone else come across this problem?
Update
This is the code used in the first tableview.
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
if ([fetchSectioningControl selectedSegmentIndex] == 2) {
return [NSString stringWithFormat:NSLocalizedString(#"%# - %d events", #"%# - %d events"), [sectionInfo name], [sectionInfo numberOfObjects]];
} else if ([fetchSectioningControl selectedSegmentIndex] == 1) {
return [NSString stringWithFormat:NSLocalizedString(#"%# - %d events", #"%# - %d events"), [sectionInfo name], [sectionInfo numberOfObjects]];
} else {
NSString *dates = [self.dateFormatter stringFromDate:date];
NSString *compareDate = [sectionInfo name];
if ([dates isEqualToString:compareDate]) {
return [NSString stringWithFormat:#"Today"];
}else {
return [sectionInfo name];
}
}
}
This is the code used in the second tableview
- (NSString *)tableView:(UITableView *)table titleForHeaderInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
NSString *dates = [self.dateFormatter stringFromDate:date];
NSString *compareDate = [sectionInfo name];
if ([dates isEqualToString:compareDate]) {
return [NSString stringWithFormat:#"Today"];
}else {
return [sectionInfo name];
}
}
I solved this issue by supplying a nil value to cacheName in the initWithFetchedRequest method of my FetchedResultsController. Another recommendation was to delete the cache before instantiating the NSFetchedResultsController using the following method:
+ (void)deleteCacheWithName:(NSString *)name;
All credit for this answer must go to 'unforgiven' for an answer posted here.