问题
As a simple example lets say I have 2 entities with a many to many relationship. The first entity is called "Projects" and the second "Employees". The project entity can have 1 or more employees and employees can be on one or more projects.
Again for simplicity Project has an attribute called projectName and a to many relationship with Employee called withEmployees. Conversely Employee has employeeName and a to many relationship to projects called myProjects.
Using NSFetchedResultsController I want a table that will have projectName as the section name and employeeName for the the rows. Using Project as the fetched entity I can easily get each project and create the appropriate section name. However, the issue comes when calculating the numberOfRowsInSection. As the fetched results pulled back one record for the project the numberofrows sees only one entity as it doesn't look into the NSSet found in the .withEmployees relationship. This means that only one employee will be output rather than the count of the number within the NSSet relationship.
How is this issue best overcome to use a to many relationship within a table?
Thanks.
Here is the code for the fetchedcontroller:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Projects"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *firstSort = [[NSSortDescriptor alloc] initWithKey:@"projectName"
ascending:NO];
NSArray *sortDescriptors = @[firstSort];
[fetchRequest setSortDescriptors:sortDescriptors];
_fetchedController = [[NSFetchedResultsController alloc]initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:@"projectName"
cacheName:nil];
回答1:
I have finally found the answer. The entity relationships I had were all fine as described above. The issue again was to get the correct number of rows returned through the to many relationship. I solved that in the following manner:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
Projects *project = [self.fetchedController objectAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:section]];
return [project.withEmployees count];
}
With the above I used the appropriate section and since I know that there is always one row that contains a set of unknown size, I created an indexpath that looked to the one and only one header record (the project) for each section. I then just looked into the NSSet that makes up the relationship and simply return the count of that. Now we get the correct number of rows per section.
As for the cells themselves:
Projects *project = [self.fetchedController objectAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:atIndex.section]];
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:atIndex];
NSArray *empArray = [project.withEmployees allObjects];
Employee *employee = [empArray objectAtIndex:atIndex.row];
cell.textLabel.text = employee.firstName;
}
The key here was to get the one section record and then pull out all of the data that makes up the to many relationship. Again I need to create an indexpath in order to only get the parent record. Now using the rows I can iterate through the array to get each element.
回答2:
How about something like
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id<NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];
return [sectionInfo numberOfObjects];
}
This assumes you've passed the correct sectionNameKeyPath: in the fetched results controller's -initWithFetchRequest:managedObjectContext:sectionNameKeypath:cacheName: call.
Side note: relationship names generally follow the same naming rules as properties or attributes. Instead of myProjects and withEmployees, use projects and employees. It makes your code read better: fred.projects instead of fred.myProjects, lawnmower.employees instead of lawnmower.myEmployees.
回答3:
I can't comment yet so asking this as an answer and possible addition to the code above:
I managed to retrieve the relationship's entity in an NSSet and show it in the tableview.
However I'm now trying to implement slide-to-delete to that tableview and my NSFRC can't register the deletion of the object in the relationship array.
In this example this would mean: I can delete a Project object, but if I delete an Employee object it crashes because it registers (0,0) rows added/removed.
I fetch it the same way and then delete that object:
NSArray *empArray = [project.withEmployees allObjects];
Employee *employee = [empArray objectAtIndex:atIndex.row];
[context deleteObject:employee];
Do you have a work around by any chance?
I currently have no predicate in my NSFRC could that change things if it checked the project.employee to something? Or should I work with 2 NSFRCs, one that loads Projects and sets sections, and one that loads Employees and loads rows? I think they might still interfere when detecting a deleted object.
来源:https://stackoverflow.com/questions/23044989/nsfetchedresultscontroller-and-tables-using-relationships