【发布时间】:2023-03-29 03:40:01
【问题描述】:
我的核心数据模型有两个实体:Author 和 Book 具有一对多关系(一位作者-> 多本书)。在主视图中,我显示了一个书籍列表,其中每个单元格都包含书名和作者姓名。该视图还分为多个部分,其中每个部分的标题都是作者姓名。 (请注意,排序描述符和 sectionNameKeyPath 都设置了“author.name”)
这是代码(为清楚起见进行了简化):
- (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];
}
现在,如果用户更改作者姓名然后返回主视图,单元格和部分将显示旧作者姓名。在网上搜索后,我发现以下代码修复了单元格中的旧作者姓名问题,但在部分标题中没有:
- (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"];
}
// save changes
NSError * error ;
if( ![self.moc save:&error] ) {
// Handle error
}
}
为什么[self.fetchedResultsController sections] 仍然包含旧的作者姓名?请帮忙!
更新 #1
本节与 Marcus 的回应 #1 相关
嗯,还是有点模糊。你是说节数不对?
部分的数量没有改变。 Sections属性数组中的对象内容不正确。
根据您发布的代码,您只是从 NSFetchedResultsController 中检索 NSManagedObject 实例。也许对那是什么有些困惑?
在代码中,我从NSFetchedResultsController 中检索NSManagedObject 实例,以便显示每个表格单元格的书名和作者姓名(当调用cellForRowAtIndexPath 时)。但是,据我所知,UITableView 中每个部分的标题并不是取自 NSManagedObject,而是取自实现 NSFetchedResultsSectionInfo 协议的 _NSDefaultSectionInfo 对象(当调用 titleForHeaderInSection 时)。
我通过我为调试编写的以下代码实现了这一点:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
id mySection = [[fetchedBooks sections] objectAtIndex:section];
NSLog(@"%@", mySection)
return [[[self.fetchedResultsController sections] objectAtIndex:section] name];
}
日志的结果是 <_nsdefaultsectioninfo:>。 Sections 属性的 NSFetchedResultsController 文档显示:
/* Returns an array of objects that implement the NSFetchedResultsSectionInfo protocol.
It's expected that developers use the returned array when implementing the following methods of the UITableViewDataSource protocol
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section;
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section;
*/
所以如果我错了请纠正我:_NSDefaultSectionInfo 不是NSManagedObject 对吗?如果是这样,当作者NSManagedObject 更改时,NSFetchedResultsController 如何检测_NSDefaultSectionInfo 对象的更改?
所以这就引出了几个问题:
您如何在其他视图中更改作者姓名?
修改作者姓名的代码写在上面saveAuthorName。 App中的流程如下:
-
从主
UITableView中,选择使用导航控制器打开新图书视图的图书单元格。 -
从书本视图中,选择选择作者,这将使用导航控制器打开新的 Select-Author 视图。 (所有作者都列在
UITableView中) -
从 Select-Author 视图中,选择使用导航控制器打开新的 Edit-Author 视图的任何作者。
-
在 Edit-Author 视图中更改作者姓名并保存关闭视图并将前一个视图(Select-Author)带入导航控制器堆栈。
-
现在可以选择不同的作者并对其进行编辑等等......直到关闭此视图。 (带来图书视图)
-
关闭图书视图,进入显示所有图书的主视图。
单元格中的作者姓名是旧的还是只是部分标题?
单元格完美更新为作者姓名(感谢saveAuthorName 中调用的willChangeValueForKey 和didChangeValueForKey)。只有节标题是旧的。
您的委托方法是什么样的?
你能具体说一下是哪一个吗?我在上面的代码部分中编写了所有与我相关的委托方法。这包括:
-
cellForRowAtIndexPath -
titleForHeaderInSection -
controllerDidChangeContent
还需要其他方法吗?
您确定您的 -[UITableViewDatasource tableView: titleForHeaderInSection:] 在您从编辑返回后正在触发?
100% 确定。 titleForHeaderInSection 带来旧值,并在保存更改后调用它。 (cellForRowAtIndexPath 在保存更改后也会调用,但会带来新值)
返回时会触发哪些 NSFetchedResultsControllerDelegate 方法?
如果您的意思是在保存时(意味着在调用 saveAuthorName 之后)调用以下方法:
-
controllerWillChangeContent:(不使用它,仅用于调试信息) -
controller:didChangeObject:(不使用,仅用于调试信息) -
controllerDidChangeContent:
如果您的意思是在返回主视图(意味着关闭书本视图)时调用以下方法:
-
cellForRowAtIndexPath -
titleForHeaderInSection -
numberOfSectionsInTableView -
numberOfRowsInSection
感谢您的帮助。谢谢!
更新 #2
您是否正在实施 -controller: didChangeSection: atIndex: forChangeType:?
是的,但在更改作者姓名时不会被解雇。 NSFetchedResultsController的当前配置如下:
- 实体:书籍
- 排序描述符:作者姓名
- sectionNameKeyPath: author.name
当NSFetchedResultsController 配置如下时,更改书名(而不是作者姓名)将触发didChangeSection 事件:
- 实体:书籍
- 排序描述符:名称
- sectionNameKeyPath: 名称
这意味着委托已正确连接到 NSFetchedResultsController。
当更改作者姓名不足以让NSFetchedResultsController 监控部分更改时,它看起来像调用[book willChangeValueForKey:@"author"] 和[book didChangeValueForKey:@"author"]。
【问题讨论】: