I have table view managed by fetched results controller.
What i want is, to change sort order when user tap segmented control.
This is how i create fetched results controller:
- (NSFetchedResultsController *)frc {
if (_frc != nil) {
return _frc;
}
NSString *sortCase;
switch (self.sortOrder) {
case tableSortServer:
sortCase = [NSString stringWithFormat:#"%#", CD_SORT];
break;
case tableSortDate:
sortCase = [NSString stringWithFormat:#"%#", CD_DATE];
break;
default:
sortCase = [NSString stringWithFormat:#"%#", CD_SORT];
break;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:CD_ENTITY_NAME inManagedObjectContext:self.getManagedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc]
initWithKey:sortCase ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:BATCH_SIZE];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.getManagedObjectContext sectionNameKeyPath:nil
cacheName:#"Root"];
self.frc = theFetchedResultsController;
_frc.delegate = self;
return _frc;
}
In segmented controller callback method i did:
- (IBAction)sortOrderChanged:(id)sender {
UISegmentedControl *sc = sender;
if (sc.selectedSegmentIndex == 0){
self.sortOrder = tableSortServer;
[self.tableView reloadData];
} else {
self.sortOrder = tableSortDate;
[self.tableView reloadData];
}
}
However, its not work. How to achieve that task? (reload table with different sort order)?
Maybe this is because of
if (_frc != nil) {
return _frc;
}
you reload the table. It uses the old fetch results controller. Your self.sortOrder = tableSortServer is therefore not taken into account. I think you need set new sort descriptor (and actually perform fetch) every time before reloading your table view
Related
I am using NSFetchedResultsController to fetch data from Core Data and to show them in UITableView. My NSFetchedResultsController looks like this:
- (NSFetchedResultsController *) fetchedResultsController
{
if (_fetchedResultsController != nil)
{
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"PlanPreparation"
inManagedObjectContext:_context];
[fetchRequest setEntity:entity];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"plan_id == %#", _planCycleID];
[fetchRequest setPredicate:pred];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects: sortDescriptor, nil];
fetchRequest.sortDescriptors = sortDescriptors;
_fetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:fetchRequest managedObjectContext:_context sectionNameKeyPath:nil cacheName:nil];
_fetchedResultsController.delegate = self;
// but if there is no results I am making another fetch by another type of NSManagedObject
if (_fetchedResultsController.fetchedObjects.count == 0)
{
NSFetchRequest *fetchRequest1 = [[NSFetchRequest alloc] init];
NSEntityDescription *entity1 = [NSEntityDescription entityForName:#"Preparation"
inManagedObjectContext:_context];
[fetchRequest1 setEntity:entity1];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"ismer == %#", [NSNumber numberWithInt:1]];
[fetchRequest1 setPredicate:pred];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects: sortDescriptor, nil];
fetchRequest1.sortDescriptors = sortDescriptors;
_fetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:fetchRequest1 managedObjectContext:_context sectionNameKeyPath:nil cacheName:nil];
_fetchedResultsController.delegate = self;
//return _fetchedResultsController;
}
return _fetchedResultsController;
}
If I am not retreiving something after first attempt to fetch, I need to set another fetch. In -cellForRowAtIndexPath I need to show data depends on type of NSManagedObjectObject:
- (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];
}
if ([[self.fetchedResultsController objectAtIndexPath:indexPath] isKindOfClass:[PlanPreparation2 class]])
{
PlanPreparation2 *planDrug = (PlanPreparation2 *)[self.fetchedResultsController objectAtIndexPath:indexPath];
CDPreparation *prep = (CDPreparation *)[[PTDataManager sharedManager]getDrugByID:planDrug.guid];
cell.textLabel.text = [NSString stringWithFormat:#"%#", prep.name];
}
else
{
CDPreparation *drug = (CDPreparation *)[self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [NSString stringWithFormat:#"%#", drug.name];
}
return cell;
}
but logging shows that -isKindOfClass: don't work here, I am not getting into quotes at all.
Question:
How can I find out type of NSManagedObjectObject?
as shown in the picture change the name and class in data model inspector of that entity
name and class field in data model inspector must be same as name of entity table.
and then after you can compare like
if([(NSManagedObject *)[self.fetchedResultsController objectAtIndexPath:indexPath] isKindOfClass:[Category class]])
as "category" is the class in my case you can choose your own as you write above [PlanPreparation2 class]
if still you face problem then comment below
I have a main tableViewController, touch a button self.navigation will push addItem viewController, click save self.navigation with pop the add VC, then we're back on the main one, I successfully add and save, also fetch, but when it comes to fetching number of cells, number returns 0, heres the method
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> secInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
NSLog(#"No. of cells determined: %lu", (unsigned long)[secInfo numberOfObjects]);
return[secInfo numberOfObjects];
}
NSLog gives me 0, whats the problem, this gave me headache.
Here's the fetch request:
- (NSFetchedResultsController*) fetchedResultsController {
// Fetch Request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Goal"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"title"
ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// setting _fethcedResultsController
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
// setting _fetchedResultsController to self
_fetchedResultsController.delegate = self; // for the tableview updating thing
// Thats it
return _fetchedResultsController;
}
Please note that when I run a check for items, it's not nil:
// fetching all items to check how many of them exists
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity: [NSEntityDescription entityForName:#"Goal"
inManagedObjectContext:self.managedObjectContext]];
NSManagedObjectContext *moc = self.managedObjectContext;
NSError *fetchError = nil;
NSArray *goals = [moc executeFetchRequest:fetchRequest error:&fetchError];
NSLog(#"No. of goals: %lu", (unsigned long)[goals count]);
// end of check for items
I forgot to put this code of line:
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
in method
- (NSFetchedResultsController*) fetchedResultsController;
so before I did, the app always creates new fetchedResultsController and over-write the old one.
I have built some code from a tutorial I found in a book. It works and I am able to display my data from CoreData successfully in a tableView. I now want to identify the data/object that the fetchRequest returns. I feel like such a dummy because I cannot understand the syntax enough to isolate my object containing my array of data. This is a snippet of the code which I am having difficulty understanding:
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Sessions" inManagedObjectContext:_context];
[fetchRequest setEntity:entity];
//NSLog(#"Entity is set to: %#", entity);
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"refid" ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
//[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:_context sectionNameKeyPath:nil cacheName:#"Root"];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [[_fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
Sessions *info = [_fetchedResultsController objectAtIndexPath:indexPath];
//Format cell data ready to be displayed
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"EE, dd LLL yyyy"];
NSString *dateString = [dateFormat stringFromDate:info.date];
NSNumber *dist1Nbr = info.dist1;
int dist1Int = [dist1Nbr integerValue];
float distIntKorM = ([dist1Nbr integerValue])/1000;
NSString *dist1StrMeters = [[NSString alloc] initWithFormat:#"%i", dist1Int];
NSString *dist1StrKorM = [[NSString alloc] initWithFormat:#"%.01f", distIntKorM];
//Select image to display
if ([info.sport isEqualToString:#"Run"]) {
UIImage *image = [UIImage imageNamed:#"trainers-15x10.png"];
cell.imageView.image = image;
cell.textLabel.text = [[NSString alloc] initWithFormat:#"%#: (%#),", dateString, info.sport];
cell.detailTextLabel.text = [[NSString alloc] initWithFormat:#"Type: %#, Dist: %#", info.sessiontype, dist1StrKorM];
} else if ([info.sport isEqualToString:#"Other"]) {
UIImage *image = [UIImage imageNamed:#"weights-15x10.png"];
cell.imageView.image = image;
cell.textLabel.text = [[NSString alloc] initWithFormat:#"%#: (%#),", dateString, info.sport];
cell.detailTextLabel.text = [[NSString alloc] initWithFormat:#"Type: %#, Dist: %#", info.sessiontype, dist1StrKorM];
}
}
If anyone can help me it would be massively appreciated.
You can access an array of NSFetchedResultsSectionInfo objects by calling:
NSArray *sections = _fetchedResultsController.sections;
Each of these objects represents a section in your table. For a given section, you can do something like:
id<NSFetchedResultsSectionInfo>section = sections[0];
NSArray *objects = section.objects;
to access the managed objects in that section. Or, if you've got an index path, you can access the associated object directly by doing:
NSManagedObject *object = [_fetchedResultsController objectAtIndexPath:indexPath];
On in reverse:
NSIndexPath *indexPath = [_fetchedResultsController indexPathForObject:object];
Is this what you're looking for?
update
Forgot this one. As Juan suggested below, you can access all fetched objects with:
NSArray *allObjects = _fetchedResultsController.fetchedObjects;
From the code you posted the NSFetchedresultsController is going to return all objects of class Sessions ordered descending by key 'refid'. NSFetchedResultsCOntroller is used together with a UITableViewController to display the results of a fetch request to the Core Data storage.
However if you want just to obtain all the objects in a array without using the table view you could do the following, for example in viewWillAppear:
if (self.managedObjectContext) {
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Sessions"];
request.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"refid" ascending:NO];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
[self.fetchedResultsController performfetch];
NSArray *results = [self.fetchedResultsController fetchedObjects]; // an array of Sessions objects ordered descending by refid key
for (Sessions *sessions in results) {
NSLog(#"Sessions = %#", sessions);
}
}
You have to make sure that the NSManagedContext is properly initialized before or nothing will be displayed.
I have two different views, controlled by two different view controllers. Each view has a UITableView inside it. Each UITableView is hooked to a NSFetchedResultsController. In View 1, I do not want to narrow my FRC results, so I do not use a predicate. In View 2, I use a predicate to get certain objects based on a BOOL (aka NSNumber!) value. But when I try to use this predicate, I get an NSRangeException (index 1 beyond bounds) when I segue to View 2. It also throws that exception when I switch the predicates (predicate on View 1, no predicate on View 2). And again the exception comes when I segue to View 2. There are cases where it is successful (neither of which is what I want):
1) I do not use any predicate for either fetch request.
2) I use the same predicate for the fetch request for both View 1 and View 2.
Some code for your enjoyment:
In View1.m:
-(NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSManagedObjectContext *context = [CoreDataHelper getContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"EditableSong" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc]
initWithKey:#"dateModified" ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:5];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:context sectionNameKeyPath:nil
cacheName:#"Root"];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
In View2.m
-(NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSManagedObjectContext *context = [CoreDataHelper getContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"EditableSong" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc]
initWithKey:#"dateModified" ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
NSPredicate *myPredicate = [NSPredicate predicateWithFormat:#"collaborative == %#", [NSNumber numberWithInt:0]];
[fetchRequest setPredicate:myPredicate];
[fetchRequest setFetchBatchSize:5];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:context sectionNameKeyPath:nil
cacheName:#"Root"];
NSLog(#"here");
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
And this is where the green error arrow (SIGABRT) points to (this code is the same in View1 and View2 by the way):
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *MyIdentifier = [NSString stringWithFormat:#"MyIdentifier %i", indexPath.row];
SongTableViewCell *cell = (SongTableViewCell *)[tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
EditableSong *song = [_fetchedResultsController objectAtIndexPath:indexPath];//<<<------HERE!!! SIGABRT
song.audioPlayer = [[AudioPlayer alloc] initWithSong:song];
cell = [[SongTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier editableSong:song andVC:self];
UIButton *playButton = [UIButton buttonWithType:UIButtonTypeCustom];
[playButton setBackgroundColor:[UIColor clearColor]];
[playButton setImage:[UIImage imageNamed:#"mainmenuplay.png"] forState:UIControlStateNormal];
playButton.frame = CGRectMake(300.0f, 15.0f, 65.0f, 65.0f);
[playButton addTarget:self action:#selector(playSong:) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:playButton];
}
return cell;
}
Thanks!
Update 1
Here is my numberOfRowsInSection method (again, same for both classes):
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
id sectionInfo =
[[_fetchedResultsController sections] objectAtIndex:section];
NSLog(#"%i", [sectionInfo numberOfObjects]);
return [sectionInfo numberOfObjects];
}
For reference, that log is supposed to print 3 in View1 and 1 in View2, but it prints 3 in both.
It seems you are returning the wrong number of cells. cellForRowAtIndexPath: wants to create a cell, but there is no objectAtIndexPath.
So you will need to check your numberOfRowsInSection method.
Problem solved:
Had to change the cache to nil.
See this post:
http://iphoneincubator.com/blog/data-management/delete-the-nsfetchedresultscontroller-cache-before-changing-the-nspredicate
i want to sort my Data in my Tableview. I have UIButtons with IBAction to call the sort.
I have created a string which contains the sortKey. I am setting the the key and call the fetchedResultsController again, to sort the tableview.
Problem is, fetchedResultsController method is not called and the sorting doesnt work.
here is my code:
- (IBAction) actionSortCardColor:(id) sender {
XLog(#"");
sortString = #"colorOrder";
[self fetchedResultsController];
[self actionRemoveSortView:sender];
}
Here my fetchedResultsController method:
- (NSFetchedResultsController *)fetchedResultsController
{
[...]
NSPredicate *inboxPred = [NSPredicate predicateWithFormat:#"archived == 0"];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
[fetchRequest setPredicate:inboxPred];
XLog(#"sortString: %#", sortString);
if (sortString == nil) {
sortString = [[NSString alloc] initWithString:#"sortingOrder"];
}
NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"setTitle" ascending:YES] autorelease];
NSSortDescriptor *sortDescriptor2 = [[[NSSortDescriptor alloc] initWithKey:sortString ascending:YES] autorelease];
NSArray *sortDescriptors = [[[NSArray alloc] initWithObjects:sortDescriptor, sortDescriptor2, nil] autorelease];
[fetchRequest setSortDescriptors:sortDescriptors];
[...]
}
Where is it that you let the table view know about these changes? You'll need to reload the data for your table view instance [tableView reloadData];
When you set the __fetchedResultsController to nil, it cannot update the UITable view with any changes because it isn't in scope.