问题
I have viewed numerous predicate questions, I have read the docs and nothing seems to be jumping out as an answer my problem.
I have a Core Data entity named Materials and I have the Attributes category, subcategory and description.
I have three UITableViewControllers
and in each I want to use a predicate to display as follows:
TableViewController 1: Only the categories (no repeating of category names)
Select a category and go to TableViewController 2.
TableViewController 2: Display Subcategories (no repeating of subcategory names)
Select a subcategory and go to TableViewController 3 listing all items in that category and subcategory.
Without using three entities in a Core Data Model, can I do this?
I have tried using the following predicate code inside my fetchedResultsController
method but to avail:
Materials * materials = [[Materials alloc]init];//this doesn't feel like it belongs inside a fetchedResultsController
NSPredicate * predicate = [NSPredicate predicateWithFormat:@"category == %@", materials.category];
fetchRequest.predicate = predicate;
This is the first time I have attempted to sort and display this way, I would usually use a predicate of a relationship as is common practice but it seemed illogical to have 3 entities for one set of data - Materials.
回答1:
You need not three different NSMO for each Materials, Category and Sub category. You only need one NSMO that is Materials which has these Attributes category, subcategory and description.
For Showing Categories in ViewController 1:
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Materials"];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Materials" inManagedObjectContext:self.managedObjectContext];
// Required! Unless you set the resultType to NSDictionaryResultType, distinct can't work.
// All objects in the backing store are implicitly distinct, but two dictionaries can be duplicates.
// Since you only want distinct Categories, only ask for the 'Category' property.
fetchRequest.resultType = NSDictionaryResultType;
fetchRequest.propertiesToFetch = [NSArray arrayWithObject:[[entity propertiesByName] objectForKey:@"Category"]];
fetchRequest.returnsDistinctResults = YES;
// Now it should yield an NSArray of distinct values in dictionaries.
NSArray *dictionaries = [self.managedObjectContext executeFetchRequest:fetchRequest error:nil];
NSLog (@"names: %@",dictionaries);
With this you can fetch all the Materials NSManagedObjects with distinct categories from Core data.
For Showing SubCategories in ViewController 2:
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Materials"];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Materials" inManagedObjectContext:self.managedObjectContext];
NSPredicate * predicate = [NSPredicate predicateWithFormat:@"SELF.category == %@", selectedCategoryName];
fetchRequest.predicate = predicate;
// Required! Unless you set the resultType to NSDictionaryResultType, distinct can't work.
// All objects in the backing store are implicitly distinct, but two dictionaries can be duplicates.
// Since you only want distinct SubCategory, only ask for the 'SubCategory' property.
fetchRequest.resultType = NSDictionaryResultType;
fetchRequest.propertiesToFetch = [NSArray arrayWithObject:[[entity propertiesByName] objectForKey:@"SubCategory"]];
fetchRequest.returnsDistinctResults = YES;
// Now it should yield an NSArray of distinct values in dictionaries.
NSArray *dictionaries = [self.managedObjectContext executeFetchRequest:fetchRequest error:nil];
NSLog (@"names: %@",dictionaries);
With this you can fetch all the Materials NSManagedObjects with distinct categories from Core data.
For third Viewcontroller to list all the items belong to a Selected category and subcategory use:
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Materials"];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Materials" inManagedObjectContext:self.managedObjectContext];
NSPredicate * predicate = [NSPredicate predicateWithFormat:@"SELF.category == %@ and SELF.subcategory == %@", selectedCategoryName,selectedSubcatgory];
fetchRequest.predicate = predicate;
// Required! Unless you set the resultType to NSDictionaryResultType, distinct can't work.
// All objects in the backing store are implicitly distinct, but two dictionaries can be duplicates.
// Now it should yield an NSArray of distinct values in dictionaries.
NSArray *dictionaries = [self.managedObjectContext executeFetchRequest:fetchRequest error:nil];
NSLog (@"names: %@",dictionaries);
回答2:
A. There is nothing wrong in having three entities. You do not have three entities for one set of data. You would have one entity (Material) for one set of data (materials), one entity for another set of data (subcategories) and one entity for another set of data (categories). (To be more precise: you would have three entity types for three types of data sets).
Modelling the structure into different entity types does not mean that you give up the data set of materials. You still have that.
http://en.wikipedia.org/wiki/Second_normal_form
B. However, if you want to have attributes instead of relationships you have to do that different.
Let's say that the category and subcategory are identified by its name. (This is another mistake, but this mistake is the consequence of the first one.)
You have to pass the name of the category selected in the first view controller to the second one. Then you can build the predicate using that name:
NSPredicate * predicate = [NSPredicate predicateWithFormat:@"category == %@", self.passedName];
回答3:
If you want to stick with category
and subcategory
as attributes rather than relationships, another option would be to use the NSFetchedResultsControllers
to derive the distinct values for you.
In your first tableViewController, specify category
as the sectionNameKeyPath
for the FRC. Then in your table view datasource methods, use the FRC's sections
property to populate your tableView and cells. For example,
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [[self.fetchedResultsController sections] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][indexPath.row];
cell.textLabel.text = sectionInfo.name
return cell;
}
And so on for other delegate/datasource methods.
Likewise in your second table view controller, you would include a predicate to restrict the fetch to those Materials with the relevant category
, and specify the FRC's sectionNameKeyPath
as subcategory
. Then use the same trick as above to populate the cells with the section data (which will in this case be the subcategories) from the FRC.
Finally, in your third table view controller, just use a predicate to restrict the results to the relevant category
and subcategory
and implement the FRC and tableView delegate/datasource methods in the normal way.
来源:https://stackoverflow.com/questions/27538930/displaying-core-data-if-attribute-has-same-name-display-once