Thanks to a more expert user, I have implemented a method to sort core data objects inside a table view section. The sorting method inside the section is different to the sorting method of the table view. The latest is done by a NSFetchedResultsController. But now I don't know how to get the values from the sorted NSArray.
This is the method that sorts the core data objects inside a section:
- (NSArray*)sortedSectionForIndex:(NSInteger)index
{
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:#"priority" ascending:YES];
id section = [[self fetchedResultsController] sections][index];
NSLog(#"INDEX********* = %ld", (long)index);
NSArray *objects = [section objects];
NSArray *sorted = [objects sortedArrayUsingDescriptors:#[sort]];
return sorted;
}
And this is the code added to the cellForRowAtIndexPat method to get the NSArray from the sortedSectionForIndex method:
NSArray *sorted = [self sortedSectionForIndex:[indexPath section]];
id object = [sorted objectAtIndex:[indexPath row]];
NSLog(#"OBJECTV= %#",object);
Now, I would need to know how to show the values from the array to be put as cell.textlabel:
A second question is that I don't know if getting the core data object from the array will change the way to handle them for the rest of the methods. I mean, before implementing this sorting method, when the user clicks on a row, a detail view from the selected object was shown. Now getting the objects from an array and not from the NSFetchedResultsController, I am not sure if it will continue working as before.
I would change the line
id object = . . .
To
NSManagedObject *object = . . .
And then
cell.textLabel = [object valueForKey:#"todoName"];
Related
I am displaying an entity called Skills in a UITableViewController.
I fetch the results like this in the viewDidLoad:
-(void)fetchTableData {
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Skills" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *error;
self.skillsArray = [[self.managedObjectContext executeFetchRequest:fetchRequest error:&error] mutableCopy];
}
Also my cell for index path is:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
// Configure the cell...
Skills *skill = self.skillsArray[indexPath.row];
// Skills is a NSManagedObject, I added the Skills.h file.
[cell.textLabel setText:skill.nameOfSkill];
return cell;
}
And I am adding new NSManagedObject *newSkill to Core Data by using UIAlertView with a text field in the delegate method:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == 1) {
NSLog(#"Add button clicked");
NSString *newSkillText = [alertView textFieldAtIndex:0].text;
NSManagedObjectContext *context = [self managedObjectContext];
Skills *newSkill = [NSEntityDescription insertNewObjectForEntityForName:#"Skills" inManagedObjectContext:context];
newSkill.nameOfSkill = newSkillText;
[self.skillsArray addObject:newSkill];
} else {
// Do something else
}
[self.tableView reloadData];
}
Every time I reload the data the cells are displaying the data in the order the data was added but if dismiss the view controller and return the cells display the data in a different order than added? The weird part is that I am using this same exact code to add core data and retrieve it in another UITableViewController and it never displays out of order. The data added in this UITableViewController is as follows: I am pushing to another UIViewController and add the information there and then dismiss back to the tableview. In this code I am adding the information while in the view controller is being presented, maybe that could have something to do with it?
Also I know I could add an NSSortDiscriptor such as:
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]initWithKey:#"nameOfSkill" ascending:YES];
NSArray *sortDescriptors = #[sortDescriptor];
but it does it by the name and I want by the way it was added without having to add another attribute for index cause I never did that to my other data and it always displays in the order it was added.
You'll have to add an Attribute to sort on...either an updating, incrementing counter, or the timestamp of the insertion. If you subclass NSManagedObject, ou can write this value in -awakeFromInsert. Then your tableview's fetch request will sort on that attribute.
You won't get the data as it is. You will have to insert a field of "Time" and you can sort according to it.
OR
Add a unique field of 'data_id' . Always check the count before inserting the data. And give the data_id accordingly adding 1 to the count. Then after fetching the data from core data sort it as per data_id.
You can do as per you like.
For the desired result you need to sort it according to timestamp or primary key which is auto-generated by core data database.
Core Data makes its own primary key - you don't have to add one. You can retrieve it with
NSManagedObjectID *moID = [managedObject objectID];
I'm new to ios development and I'm trying to sort a tableview that gets populated by an array. I've looked into other solutions and for some reason the tableview isn't populating correctly. The array sorts in ascending order, but then the tableview doesn't display the objects in the correct order.
Here is what I have for the sorting:
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"_miles"
ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray = [_addObjects sortedArrayUsingDescriptors:sortDescriptors];
_objects = [NSMutableArray arrayWithArray:sortedArray];
Here is my cellforrow :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//NSLog(#"%#", _objects);
VenuesTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
CustomObject *currentObject = [_objects objectAtIndex:indexPath.row];
cell.venuesTitle.text = [currentObject nameOfVenue];
NSString *preDistance = #"about";
NSString *postDistance = #"miles";
cell.venuesSubTitle.text = [NSString stringWithFormat:#"%# %# %#", preDistance,[_miles objectAtIndex:indexPath.row] ,postDistance];
if (cell != nil) {
NSMutableString *imagePath = [NSMutableString stringWithFormat:#"%#", [_paths objectAtIndex:indexPath.row]];
[cell.venuesImage setContentMode:UIViewContentModeScaleAspectFit];
[cell.venuesImage setImageWithURL:[NSURL URLWithString:imagePath] placeholderImage:[UIImage imageNamed:#"placeholder#2x.png"]];
}
return cell;
}
when I log out the _objects array I get them in the right order, but they're not appearing in the right order in the tableview (the objects have a miles property that I'm trying to sort by).
You are sorting your array, _addObjects by miles and storing the result in _objects, but then, when you're displaying the results, you're not accessing miles from that sorted array you just created, but rather looking it up in some other array.
I don't know when you're populating that separate _miles array, but I'd suggest you retire it entirely and just make sure you get and set the miles property from the CustomObject instances. (Same for _paths ... this should probably be a property of your CustomObject, not a separate array.)
I don't see anything obvious, but a couple things that are a bit strange:
You should not use _objects. Always use self.objects instead. Most programming languages make it completely impossible to access pointers directly like you are doing here, and for good reason. It's dangerous and can lead to confusing bugs like this one. You should pretty much never access a property using _foobar. Always use self.foobar.
Why are you using NSMutableArray instead of NSArray? Are you sure there isn't code somewhere else modifying it?
Where does VenuesTableViewCell *cell get created? I don't see anywhere in your code that one of these objects is created. [tableView dequeueReusableCellWithIdentifier..] never creates a new cell, it always returns an existing one or nil if none exists. Your code isn't throwing an assertion error (I assume?), so obviously it never returns nil. But there's something weird going on if that doesn't return nil the first time you call it?
In a previous question, a user asked for a method to order core data objects inside a UITableView Section. I am in the same case, but I don't find the way to implement the proposed solution.
The proposed solution was to create the following method:
- (NSArray*)sortedSectionForIndex:(NSInteger)index
{
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:#"priority" ascending:YES];
id section = [[self fetchedResultsController] sections][index];
NSLog(#"INDEX********* = %ld", (long)index);
NSArray *objects = [section objects];
NSArray *sorted = [objects sortedArrayUsingDescriptors:#[sort]];
return sorted;
}
But I don't know how to call this method inside my code in the way that the section objects would be ordered.
Thank you.
You should implement separate fetchedResultsController(s) and sort descriptor for the section(s) you want sorted differently. Take a look at the CompanyMenuTableViewController code in this sample app. One of the sections uses it's own fetchedResultsController. You would use a similar approach to handle each section independently of other sections, with each section having its own fetchedResultsController if necessary.
Make sure you download the most recent version.
http://ossh.com.au/design-and-technology/software-development/sample-library-style-ios-core-data-app-with-icloud-integration/
I have a list of managed objects stored in core data. I use these objects to populate a tableview controller that is sectioned according to alphabetical order. The data in these objects is obtained via web service, so I have no control over their case (which really doesn't make much difference in this case).
Most of the data is returned in all caps. I've noticed that, on the rare occasions where the case is NOT all caps, those items do not fall into alphabetic order. In the following code sample, stationIndex is an array of sorted first letters:
for(NSString *character in stationIndex){
NSPredicate *pred = [NSPredicate predicateWithFormat:#"name beginswith[c] %#", character];
// sort the list
NSArray *filteredGaugeList = [[tempGaugeList filteredArrayUsingPredicate:pred] sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
NSString *first = [(Gauge*)a name];
NSString *second = [(Gauge*)b name];
return [first compare:second];
}];
if([filteredGaugeList count] > 0){
[[self allGauges] addObject:filteredGaugeList];
}
}
I'm aware that there is a way to ignore case when using a selector, but in my case, I'm sorting on properties of objects, so I'm assuming I need a comparator. Is there a way to handles case in this situation? Thanks!
You can sort ignoring case in a comparator as well, just use
return [first caseInsensitiveCompare:second];
Alternatively, use a sort descriptor specifying the selector and the sort key:
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:#"name"
ascending:YES
selector:#selector(caseInsensitiveCompare:)];
NSArray *sorted = [array sortedArrayUsingDescriptors:#[sort]];
Remark: To display Core Data objects in a table view, you can also use
NSFetchedResultsController. Then you would add the predicate and the sort
descriptor to the fetch request. A fetched results controller has also methods
to group a table view into sections, and to update a table view automatically
when objects are inserted/deleted/modified.
I have a UITableView that adds information from a Core Data the following way:
The "Category" for various names is added at the header
The names that correspond to the Category should be loaded in cells beneath the Category
Right now I have the header loading the right name and the right number of sections and rows being added. However - the results from Core Data are being returned as an NSSet, so I can't add each name to the cells based on the indexPath.row.
Any suggestions? For reference:
cell.textLabel.text = [NSString stringWithFormat:#"%#",[[[dataToUse objectAtIndex:indexPath.section]valueForKey:#"heldBy"]valueForKey:#"name"]];
Returns the appropriate set of names to each cell of the appropriate section, but it returns the ENTIRE set to each cell. I just want each name to be added based on which row it's a part of. I can solve this by converting the NSSet to an Array, but since there are multiple sets being created (because there are multiple categories) I don't see how I can do this.
EDIT: I fixed my problem by doing the following, but I'm still interested to know what the best thing to do would have been.
NSSet *daset = [[dataToUse objectAtIndex:indexPath.section]valueForKey:#"heldBy"];
NSMutableArray *addToLabel = [[NSMutableArray alloc]init];
int i = 0;
for(NSSet *contact in daset) {
[addToLabel insertObject:[contact valueForKey:#"name"] atIndex:i];
NSLog(#"%#",addToLabel);
i++;
}
cell.textLabel.text = [NSString stringWithFormat:#"%#",[addToLabel objectAtIndex:indexPath.row]];
Use an NSFetchedResultsController
It's designed to do exactly what you're looking for. For straight forward cases like yours, where you just need your data organized into sections based on your model relationships it will offload a lot of weight off your shoulders by automatically managing the fetching, editing, caching etc. You can find a nice tutorial here and of course the official documentation here.
To convert from NSSet->NSArray you need to sort the set with a NSSortDescriptior. Something like:
NSSortDescriptor *sort=[[NSSortDescriptor alloc] initWithKey:#"nm" ascending:YES selector:#selector(localizedCaseInsensitiveCompare:)];
NSArray *arr = [[myset allObjects] sortedArrayUsingDescriptors:[NSArray arrayWithObject:sort]];
NSSortDescriptor *sort=[[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES selector:#selector(localizedCaseInsensitiveCompare:)];
NSArray *arr = [[myset allObjects] sortedArrayUsingDescriptors:[NSArray arrayWithObject:sort]];
"nsset object" *nssetObject =[arr objectAtIndex:indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:#"%#",nssetObject.name];