NSFetchedResultsController 在启动后不更新 UITableView
Posted
技术标签:
【中文标题】NSFetchedResultsController 在启动后不更新 UITableView【英文标题】:NSFetchedResultsController doesn't update UITableView after launch 【发布时间】:2013-10-16 02:40:01 【问题描述】:我有一个简单的基于 UIManagedDocument 的核心数据应用程序。我是在 ios 中编程的新手,我几乎没有开始使用它,我被困住了。我将 People NSManagedObjects 存储在核心数据中。当我第一次启动应用程序时,那里没有任何条目,但是如果我向上滑动一个模态 VC 并关闭它,它们就会神奇地出现。我的设计大致基于 iTunes U/Sanford 课程中的“Photomania”应用程序,但使用单例 CoreDataStore 对象来处理获取 managedObjectContext。这是我的 CoreDataStore 的代码:
+ (CoreDataStore *)sharedStore
static CoreDataStore *sharedStore = nil;
if (!sharedStore)
sharedStore = [[super allocWithZone:nil] init];
return sharedStore;
+ (instancetype)allocWithZone:(struct _NSZone *)zone
return [self sharedStore];
- (NSManagedObjectContext *)getContext
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory
inDomains:NSUserDomainMask] lastObject];
url = [url URLByAppendingPathComponent:@"dbDocument"];
UIManagedDocument *document = [[UIManagedDocument alloc] initWithFileURL:url];
if (![[NSFileManager defaultManager] fileExistsAtPath:[url path]])
NSLog(@"No file exists...");
[document saveToURL:url forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success)
NSLog(@"Document saveForCreating completionHandler invoked. Success: %hhd", success);
if (success)
self.managedObjectContext = document.managedObjectContext;
NSLog(@"Got context for created file!!");
];
else if (document.documentState == UIDocumentStateClosed)
NSLog(@"Document state is closed. documentState == %u", document.documentState);
[document openWithCompletionHandler:^(BOOL success)
NSLog(@"Document opening success: %hhd", success);
if (success)
self.managedObjectContext = document.managedObjectContext;
NSLog(@"Got context for now-opened file!!");
];
else
self.managedObjectContext = document.managedObjectContext;
NSLog(@"Got context for already-opened file!!");
return self.managedObjectContext;
这是来自 PeopleListViewController 的代码:
- (void)setManagedObjectContext:(NSManagedObjectContext *)managedObjectContext
_managedObjectContext = managedObjectContext;
if (managedObjectContext)
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Person"];
request.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"name"
ascending:YES
selector:@selector(localizedCaseInsensitiveCompare:)]];
request.predicate = nil; // all Persons
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:nil];
else
self.fetchedResultsController = nil;
- (void)viewWillAppear:(BOOL)animated
[super viewWillAppear:animated];
if (!self.managedObjectContext)
[self setManagedObjectContext:[[CoreDataStore sharedStore] getContext]];
这是启动后的结果:
这是模态VC:
这是在关闭模态 VC 之后(点击取消):
我尝试在 managedObjectContext 上使用 performFetch,甚至将其延迟到 Core Data 文档报告它已打开之后。没有任何效果。任何帮助将不胜感激。
【问题讨论】:
【参考方案1】:粗略浏览一下您的代码,我注意到您正在异步创建上下文。这很好,但是当上下文准备好时,您需要通知表视图控制器并让它调用 reloadData。
数据存储中的完成处理程序需要在表视图控制器上设置上下文属性。
【讨论】:
我该怎么做。我确定这是需要在完成处理程序中进行的操作,但我不确定该怎么做。这听起来可能是问题所在。我做了一个测试,发现[fetchedObjects count]
最初是0,然后在关闭模态VC后是5。【参考方案2】:
NSFetchedResultsController
不会神奇地更新您的 tableView。我发现 Photomania 有点复杂,因为像 CoreDataViewController 这样的辅助类很少。因此 tableVew 只能通过调用 reloadData
方法或使用一对 beginUpdates
-endUpdates
重新加载。而NSFetchedResultsController
只能通过调用performFetch:
方法来更新。
我一般用NSManagedObjectContext
getter:
- (NSFetchedResultsController*)fetchedResultsController
if (!_fetchedResultsController)
NSFetchRequest* fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"some entity name"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"some predicate"];
fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]];
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.context sectionNameKeyPath:@"name" cacheName:nil];
_fetchedResultsController.delegate = self;
NSError* error;
[_fetchedResultsController performFetch:&error];
if (error)
NSLog(@"Fetch ERROR. %@", error);
return _fetchedResultsController;
在这里我将NSFetechedResultsControllerDelegate
设置为self。和performFetch:
获取数据。我实现了委托的方法:
- (void)controllerWillChangeContent:(NSFetchedResultsController*)controller
[self.tableView beginUpdates];
- (void)controller:(NSFetchedResultsController*)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
switch(type)
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationFade];
break;
- (void)controller:(NSFetchedResultsController*)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath*)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath*)newIndexPath
UITableView *tableView = self.tableView;
switch(type)
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath]
atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
- (void)controllerDidChangeContent:(NSFetchedResultsController*)controller
[self.tableView endUpdates];
【讨论】:
我在viewWillAppear
后面加了一个performFetch:
,但是结果是一样的。 beginUpdates
/endUpdates
不起作用,因为它是应用程序启动的时候。未进行任何更新。
@LouValencia,尝试在fetchedResultsController
setter 中使用performFetch:
。所以你会在必要时performFetch:
,例如numberOfRowsInSection:
。如果您的存储发生变化,NSFetchedResultsControllerDelegate
方法将更新您的 tableView以上是关于NSFetchedResultsController 在启动后不更新 UITableView的主要内容,如果未能解决你的问题,请参考以下文章