My Core Data model has two entities: Author
and Book
with the To-Many relationship (one author → many books). The main view displays a list of books in which each cell contains the name of the book and the name of the author. The presentation is also divided into sections, where each section title is the name of the author. (note that "author.name" is set for both the sort descriptor and sectionNameKeyPath)
Here is the code (simplified for clarity):
- (NSFetchedResultsController *)fetchedResultsController { if (__fetchedResultsController != nil) { return __fetchedResultsController; } NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease]; NSEntityDescription *entity = [NSEntityDescription entityForName:@"Book" inManagedObjectContext:self.managedObjectContext]; [fetchRequest setEntity:entity]; NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"author.name" ascending:YES] autorelease]; NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil]; [fetchRequest setSortDescriptors:sortDescriptors]; NSFetchedResultsController *aFetchedResultsController = [[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"author.name" cacheName:nil] autorelease]; aFetchedResultsController.delegate = self; self.fetchedResultsController = aFetchedResultsController; NSError *error = nil; [self.fetchedResultsController performFetch:&error]; return __fetchedResultsController; } - (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] autorelease]; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; } Book* book = [self.fetchedResultsController objectAtIndexPath:indexPath]; cell.textLabel.text = [NSString stringWithFormat:@"%@ %@", book.name, book.author.name]; return cell; } - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { return [[[self.fetchedResultsController sections] objectAtIndex:section] name]; } - (void)controllerDidChangeContent:(NSFetchedResultsController*)controller { [self.tableView reloadData]; }
Now, if the user changes the name of the author and then returns to the main view, the cells and sections will display the old name of the author. After searching the Internet, I found the following code that fixes the problem with the name of the old author in the cells, but not in the section headers:
- (void)saveAuthorName:(NSString *)newName { for (Book* book in author.books) { [book willChangeValueForKey:@"author"]; } author.name = newName; for (Book* book in author.books) { [book didChangeValueForKey:@"author"]; }
Why [self.fetchedResultsController sections]
contain old author names? Please, help!
Update # 1
This section applies to Response # 1 of Marcus
Hmmm, still a little fuzzy. Are you saying that the number of partitions is incorrect?
The number of sections has not been changed. The contents of the objects in the Sections
property array are incorrect.
Based on your code, you simply extract instances of NSManagedObject from NSFetchedResultsController. Perhaps there is some confusion about what it is?
In the code, I extract NSManagedObject
instances from NSFetchedResultsController
to display the book name and author name for each cell in the table (when cellForRowAtIndexPath
is cellForRowAtIndexPath
). However, the titles of each section in a UITableView
not accepted from NSManagedObject
in my understanding, but are taken from the _NSDefaultSectionInfo
object, which implements the NSFetchedResultsSectionInfo
protocol (when titleForHeaderInSection
is titleForHeaderInSection
).
I realized this with the following code that I wrote for debugging:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { id mySection = [[fetchedBooks sections] objectAtIndex:section]; NSLog(@"%@", mySection) return [[[self.fetchedResultsController sections] objectAtIndex:section] name]; }
The log result was <_NSDefaultSectionInfo: 0x8462b90>. The NSFetchedResultsController documentation for the Sections property shows:
So please correct me if I am wrong: _NSDefaultSectionInfo
not NSManagedObject
right? If so, how NSFetchedResultsController
detect changes for _NSDefaultSectionInfo
objects when Author NSManagedObject
changes?
So this leads to two questions:
How do you change the author name in this other view?
The code for changing the author’s name is written above in saveAuthorName
. The flow in the application is as follows:
From the main UITableView
, by selecting a book cell that opens a new book view using the navigation controller.
In book viewing mode, select an author who opens a new selection-author view using the navigation controller. (all authors are listed in UITableView
)
In the "Select-author" window, select any author who opens a new "Editor-author" view using the navigation controller.
In the Editor-Author view, the author and save name change that closes the view and displays the previous Select-Author view in the navigation controller stack.
Now you can select another author and edit him, etc. before closing this view. (Leads a book look)
The “Closing a book” screensaver brings up the main view, where all books are displayed.
Is the author name in the cell old or just the section heading?
The cell is updated perfectly with the name of the author (thanks to willChangeValueForKey
and didChangeValueForKey
called in saveAuthorName
). The heading is old.
What do your delegate methods look like?
Could you indicate which one? I wrote all the delegate methods that look relevant to me in the code section above. It includes:
Any other method required?
Are you sure your - [UITableViewDatasource tableView: titleForHeaderInSection:] is started after you return from editing?
100% sure. titleForHeaderInSection
returns the old values and is called after the changes are saved. ( cellForRowAtIndexPath
also called after saving changes, but brings new values)
What methods of NSFetchedResultsControllerDelegate are triggered upon return?
If you mean when saving (means after calling saveAuthorName
), the following methods are called:
controllerWillChangeContent:
(not using it, just for debugging information)
controller:didChangeObject:
(not using it, just for debugging information)
controllerDidChangeContent:
If you want to return to the main view (meaning closing the book view), the following methods are called:
I appreciate your help. Thanks!
Update # 2
Are you using -controller: didChangeSection: atIndex: forChangeType :?
Yes, but this does not work when changing the name of the author. The current configuration for NSFetchedResultsController
as follows:
- Essence: Book
- Descriptor Sort: author.name
- sectionNameKeyPath: author.name
Changing the name of the book (rather than the name of the author) will trigger the didChangeSection
event when the NSFetchedResultsController
configured as follows:
- Essence: Book
- Descriptor Sort: Name
- sectionNameKeyPath: name
This means that the delegate is correctly connected to the NSFetchedResultsController.
It looks like calling [book willChangeValueForKey:@"author"]
and [book didChangeValueForKey:@"author"]
when changing the author name is not enough for NSFetchedResultsController
to control section changes.