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.
Related
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
I'm doing a Core Data tutorial and I keep getting a crash. It's a objc_exception_throw.
I create a method called loadTableData and call it in viewDidLoad
-(void)loadTableData{
NSManagedObjectContext *context = [[self appDelegate]managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Label" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"label like %#", [context objectWithID: self.labelID]];
[fetchRequest setPredicate:predicate];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]initWithKey:#"name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc]initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSError *error = nil;
self.artistArray = [context executeFetchRequest:fetchRequest error:&error];
[self.tableView reloadData];
}
It gets stuck here
self.artistArray = [context executeFetchRequest:fetchRequest error:&error];
Commenting out the predicate alloc/init and setPredicate method call results in an app that doesn't crash, but doesn't do what I want.
See entities and relationships below.
In LabelViewController here is additional code to show how [context objectWithID: self.labelID] is set
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
MLBLArtistViewController *artistViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"ArtistViewController"];
Label *label = [self.labelArray objectAtIndex:indexPath.row];
artistViewController.labelID = [label objectID];
[self.navigationController pushViewController:artistViewController animated:YES];
}
I'd use a CONTAINS instead:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"label CONTAINS[cd] %#", [context objectWithID: self.labelID]];
You can use a LIKE but I like the simplicity of the CONTAINS, see this quesiton:
NSPredicate that is the equivalent of SQL's LIKE
First of all, it seems that you want to fetch "Artist" objects, not "Label" objects:
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Artist" inManagedObjectContext:context];
Next, LIKE or CONTAINS are for testings strings, you need a simple ==:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"label == %#", [context objectWithID:self.labelID]];
Remark: It would be simpler to pass the label object itself to the pushed view controller,
instead of [label objectID].
I have a UITableView using NSFetchedResultsController to display a list of users.
I added a UISegmentedControl to switch between my full list of users and only my active users.
To get my list of users, I use fetchedResultsController :
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"User" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"lastname" ascending:YES];
NSArray *sortDescriptors = #[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
if (self.barSegmentedControl.selectedSegmentIndex == 1) {
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"active == YES"]];
}
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"Master"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return _fetchedResultsController;
}
To perform an update of the tableview when clicking on the UISegmentedControl, I use segmentedControlIndexChanged :
-(IBAction) segmentedControlIndexChanged{
self.fetchedResultsController = nil;
[self fetchedResultsController];
[self.tableView reloadData];
}
But I'm not sure I'm doing this right.
Could you say me if this is the right way to filter a UITableView with NSFetchedResultsController ?
I also wanted to know if it is possible to filter a list with an animation ?
Exactly like in the iPhone Phone App when in the Recents Tab, it switch from all calls to missed calls ?
Thanks for your help.
Your approach will work fine. I too using the same thing without any issues.
Coming to animation you can do them in its delegate methods,
Check Apple's Documentation here.
self.fetchedResultsController = nil; is a reasonable way to invalidate a property.
But you should delete this weird [self fetchedResultsController]; line.
[self.tableView reloadData]; will call your delegate which should access fetchedResultsController property (if you're using _fetchedResultsController variable in your datasource methods, that would be an anti-pattern).
filter a list with an animation
Prepare a list of rows to hide and send
[self.tableView deleteRowsAtIndexPaths: ... withRowAnimation:...]
instead of reloadData.
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.