我可以在我的应用程序中创建新的托管对象,在一个单独的视图中我有一个表格视图,显示我拥有的所有对象。
当我创建一个新的托管对象(在一个完全独立的视图中)时,我发出一个通知,试图让我的表视图重新加载包含我刚制作的新对象的新数据。
我没有编辑表格视图。
只有一个托管上下文。
我无法理解我做错了什么。如果我关闭应用程序并重新启动我的新对象是在表视图中。当我收到通知时,我正在尝试调用reloadData,并且我还尝试再次执行获取,但我最终得到的只是一个空表视图。
在代码方面,我的ListViewController:
@interface MyNotesViewController : UIViewController
{
NSManagedObjectContext * __managedObjectContext;
UITableView * _notesTableView;
MyNotesTableViewDelegate_DataSource * _tableDelegate_DataSource;
}
我的viewDidLoad看起来像这样:
- (void)viewDidLoad
{
[super viewDidLoad];
_tableDelegate_DataSource = [[MyNotesTableViewDelegate_DataSource alloc] init];
_tableDelegate_DataSource.managedObjectContext = __managedObjectContext;
[_notesTableView setDataSource:_tableDelegate_DataSource];
NSError * _coreDataError;
BOOL success = [[_tableDelegate_DataSource fetchedResultsController] performFetch:&_coreDataError];
if(success){
[_notesTableView reloadData];
}
}
在我的委托/数据源对象中,我有:
@interface MyCommutesTableViewDelegate_DataSource : NSObject
<UITableViewDataSource, NSFetchedResultsControllerDelegate>
{
NSManagedObjectContext * __managedObjectContext;
NSFetchedResultsController * __fetchedResultsController;
}
@property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
@end
实施摘录:
-(NSFetchedResultsController*)fetchedResultsController{
if(!__fetchedResultsController){
NSFetchRequest * request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:@"Note" inManagedObjectContext:__managedObjectContext]];
[request setFetchBatchSize:10];
NSSortDescriptor * sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"noteDate" ascending:NO];
[request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[sortDescriptor release];
__fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:__managedObjectContext sectionNameKeyPath:nil cacheName:nil];
__fetchedResultsController.delegate = self;
[request release];
}
return __fetchedResultsController;
}
我在这里有stander UITableView数据源方法:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
id <NSFetchedResultsSectionInfo> sectionInfo =
[[__fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"NotesTableViewCell";
NotesTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray * topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"NotesTableViewCell" owner:nil options:nil];
for(id currentObject in topLevelObjects){
if([currentObject isKindOfClass:[UITableViewCell class]]){
cell = (NotesTableViewCell*) currentObject;
break;
}
}
}
// Configure the cell.
Note * _note = [__fetchedResultsController objectAtIndexPath:indexPath];
[...]
return cell;
}
更新
我还原到只是请求所有对象,因为我必须快速修复我的问题,然后我意识到,在我保存上下文之后,我之前在应用程序启动时使用的获取请求,现在没有返回数据,没有错误但它什么都不返回。
所以我猜我实现NSFetchResultsController的方式没有问题。但是在保存新对象后,请求怎么能开始不返回结果呢?
注意如果重新启动,我仍会获得所有对象,包括新添加的对象。
标准要求:
-(NSArray*)getNotes{
NSFetchRequest * request = [[NSFetchRequest alloc] init];
NSEntityDescription * entity = [NSEntityDescription entityForName:@"Note" inManagedObjectContext:[self managedObjectContext]];
[request setEntity:entity];
NSSortDescriptor * sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"noteDate" ascending:NO];
[request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[sortDescriptor release];
NSError * coreDataError;
NSArray * fetchedObjects = [[self managedObjectContext] executeFetchRequest:request error:&coreDataError];
[request release];
if(fetchedObjects != nil){
return fetchedObjects;
} else {
NSLog(@"ERR: %@", coreDataError);
return nil;
}
}
我正在等待通知告诉我已将一个新对象添加到Core Data,然后我调用上面的方法然后在我的tableview上调用reloadData ...
答案 0 :(得分:27)
您将UITableViewController
设为NSFetchedResultsControllerDelegate
。非常好。现在尝试在TableViewController中实现controllerDidChangeContent:
方法,如下所示:
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView reloadData];
}
您的NSFetchedResultsController
将侦听Core Data中已移除或新的对象,并通知其委托(您的TableViewController)更改。检查Xcode中的Core Data模板项目,通过UITableView
中的添加和删除动画更好地实现这一点。
答案 1 :(得分:2)
请务必添加didChangeObject方法:
- (void)controller:(NSFetchedResultsController *)controller
didChangeObject:(id) anObject
atIndexPath:(NSIndexPath *)indexPath
forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
switch(type)
{
case NSFetchedResultsChangeInsert:
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[self.tableView
cellForRowAtIndexPath:indexPath]
atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
[self.tableView insertRowsAtIndexPaths:[NSArray
arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
NSManagedObject *note = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [note valueForKey:@"title"];
}
此后,新的托管对象显示在表格视图中。
答案 2 :(得分:0)
如果有人遇到同样的问题,就像我刚才那样。我用绿色勾选了上面的解决方案,但它对我不起作用。我跟着Apple's code,一切都很顺利。
简单地说,只需实现三个“Fetchcontroller”委托函数
func controllerWillChangeContent(controller: NSFetchedResultsController) {
self.tableView.beginUpdates()
}
func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
switch type {
case .Insert:
self.tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)
case .Delete:
self.tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
case .Update:
print("")
self.configureCell(self.tableView.cellForRowAtIndexPath(indexPath!)!, indexPath: indexPath!)
case .Move:
self.tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
self.tableView.insertRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
}
}
func controllerDidChangeContent(controller: NSFetchedResultsController) {
self.tableView.endUpdates()
}
答案 3 :(得分:0)
已更新为最新的Swift 4.2和Xcode 10。
extension MyListController: NSFetchedResultsControllerDelegate {
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
self.tableView.beginUpdates()
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type {
case .insert:
self.tableView.insertRows(at: [newIndexPath!], with: .fade)
case .delete:
self.tableView.deleteRows(at: [indexPath!], with: .fade)
case .update:
self.tableView.reloadRows(at: [indexPath!], with: .fade)
case .move:
self.tableView.deleteRows(at: [indexPath!], with: .fade)
self.tableView.insertRows(at: [indexPath!], with: .fade)
}
}
func controller(_ controller:
NSFetchedResultsController<NSFetchRequestResult>, didChange
sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex
sectionIndex: Int, for type: NSFetchedResultsChangeType) {
switch (type) {
case .insert:
self.tableView.insertSections([sectionIndex], with: .fade)
case .delete:
self.tableView.deleteSections([sectionIndex], with: .fade)
case .move:
self.tableView.deleteSections([sectionIndex], with: .fade)
self.tableView.insertSections([sectionIndex], with: .fade)
case .update:
self.tableView.reloadSections([sectionIndex], with: .fade)
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
self.tableView.endUpdates()
}
}