【发布时间】:2014-01-22 09:10:57
【问题描述】:
我在 UITableViewController 子类中显示了一个名为 GarmentTypes 的核心数据实体,该子类实现了 NSFetchedResultsControllerDelegate,并有一个 NSFetchedResultsController 作为属性。模态序列提供了一个新的视图控制器,可以在其中添加新的 GarmentType。关闭模式视图控制器后,底层 UITableViewController 不会更新。我知道 GarmentType 实体保存在数据库中,因为如果我退回到前一个视图控制器然后重新排序,它就会显示出来。您可以在此处观看演示视频:https://www.youtube.com/watch?v=DahKMgVxDho
我创建了一个名为 CoreDataTableViewController 的通用类,它实现了 NSFetchedResultsControllerDelegate 并具有 NSFetchedResultsController 属性:
@interface CoreDataTableViewController : UITableViewController <NSFetchedResultsControllerDelegate>
@property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;
我将 CoreDataTableViewController 子类化为 GarmentTypesCDTVC 以显示 GarmentTypes。它有一个属性,NSManagedObjectContext:
@interface GarmentTypesCDTVC : CoreDataTableViewController
@property (strong, nonatomic) NSManagedObjectContext *context;
在其 viewWillAppear 中检索上下文:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[DressyHelper openDocumentUsingBlock:^(UIManagedDocument *document){
self.context = document.managedObjectContext;
}];
}
获取请求是在上下文的设置器时创建的:
- (void)setContext:(NSManagedObjectContext *)context
{
_context = context;
if (context) {
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"GarmentType"];
request.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)]];
request.predicate = nil; // All GarmentTypes
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];
} else {
self.fetchedResultsController = nil;
}
}
DressyHelper 分发 UIManagedDocument:
// Open or create a UIManagedDocument.
+ (void)openDocumentUsingBlock:(completion_block_t)completionBlock;
{
// Get documents directory and path.
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
url = [url URLByAppendingPathComponent:@"DressyDocument"];
// Create the document and open if a match exists on file.
UIManagedDocument *document = [[UIManagedDocument alloc] initWithFileURL:url];
if ([[NSFileManager defaultManager] fileExistsAtPath:[url path]]) {
[document openWithCompletionHandler:^(BOOL success) {
if (!success) NSLog (@"Couldn't open document at %@", url);
else completionBlock(document);
}];
} else {
// No match exists, so save the document to file.
[document saveToURL:url forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
if (!success) NSLog(@"Couldn't create document at %@", url);
else completionBlock(document);
}];
}
}
我已经阅读了类似的问题并尝试实施但没有成功,所以如果这需要类似的解决方案,我深表歉意,但我已经为此绞尽脑汁了一段时间。例如,放置一个 [self.tableView reloadData];在 GarmenttypesCDTV 的 viewWillAppear: 中没有效果。
TIA
更新:这里是 CoreDataTableViewController 的 .m:
//
// CoreDataTableViewController.m
// Dressy
//
// Created by Michael Mangold on 9/6/13.
// Copyright (c) 2013 Michael Mangold. All rights reserved.
//
#import "CoreDataTableViewController.h"
@interface CoreDataTableViewController()
@property (nonatomic) BOOL beganUpdates;
@end
@implementation CoreDataTableViewController
#pragma mark - Properties
@synthesize fetchedResultsController = _fetchedResultsController;
@synthesize suspendAutomaticTrackingOfChangesInManagedObjectContext = _suspendAutomaticTrackingOfChangesInManagedObjectContext;
@synthesize debug = _debug;
@synthesize beganUpdates = _beganUpdates;
#pragma mark - Fetching
- (void)performFetch
{
if (self.fetchedResultsController) {
if (self.fetchedResultsController.fetchRequest.predicate) {
if (self.debug) NSLog(@"[%@ %@] fetching %@ with predicate: %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), self.fetchedResultsController.fetchRequest.entityName, self.fetchedResultsController.fetchRequest.predicate);
} else {
if (self.debug) NSLog(@"[%@ %@] fetching all %@ (i.e., no predicate)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), self.fetchedResultsController.fetchRequest.entityName);
}
NSError *error;
[self.fetchedResultsController performFetch:&error];
if (error) NSLog(@"[%@ %@] %@ (%@)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), [error localizedDescription], [error localizedFailureReason]);
} else {
if (self.debug) NSLog(@"[%@ %@] no NSFetchedResultsController (yet?)", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
}
[self.tableView reloadData];
}
- (void)setFetchedResultsController:(NSFetchedResultsController *)newfrc
{
NSFetchedResultsController *oldfrc = _fetchedResultsController;
if (newfrc != oldfrc) {
_fetchedResultsController = newfrc;
newfrc.delegate = self;
if ((!self.title || [self.title isEqualToString:oldfrc.fetchRequest.entity.name]) && (!self.navigationController || !self.navigationItem.title)) {
self.title = newfrc.fetchRequest.entity.name;
}
if (newfrc) {
if (self.debug) NSLog(@"[%@ %@] %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), oldfrc ? @"updated" : @"set");
[self performFetch];
} else {
if (self.debug) NSLog(@"[%@ %@] reset to nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
[self.tableView reloadData];
}
}
}
#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [[[self.fetchedResultsController sections] objectAtIndex:section] name];
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
return [self.fetchedResultsController sectionForSectionIndexTitle:title atIndex:index];
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
return [self.fetchedResultsController sectionIndexTitles];
}
#pragma mark - NSFetchedResultsControllerDelegate
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext) {
[self.tableView beginUpdates];
self.beganUpdates = YES;
}
}
- (void)controller:(NSFetchedResultsController *)controller
didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex
forChangeType:(NSFetchedResultsChangeType)type
{
if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext)
{
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
{
if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext)
{
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.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeMove:
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
if (self.beganUpdates) [self.tableView endUpdates];
}
- (void)endSuspensionOfUpdatesDueToContextChanges
{
_suspendAutomaticTrackingOfChangesInManagedObjectContext = NO;
}
- (void)setSuspendAutomaticTrackingOfChangesInManagedObjectContext:(BOOL)suspend
{
if (suspend) {
_suspendAutomaticTrackingOfChangesInManagedObjectContext = YES;
} else {
[self performSelector:@selector(endSuspensionOfUpdatesDueToContextChanges) withObject:0 afterDelay:0];
}
}
@end
【问题讨论】:
-
附注:您可能希望将“完成”按钮放在右上角,将“取消”按钮放在左上角。
-
设置断点以查看调用了哪些 NSFetchedResultsController 委托方法。
-
为什么要在 viewWillAppear 中打开 UIManagedDocument ?这样做似乎很奇怪,因为这可能会被多次调用,我希望您只在应用程序启动时执行一次,然后在您访问核心数据对象的每个模块中使用上下文。您正在打开文档,然后转到另一个视图以创建一个新对象,然后当您返回上一个视图时,您最终会再次打开文档?您是否在这些转换中的某处关闭文档?
-
不,我不会关闭文档。在 viewDidLoad 中打开文档对我遇到的问题没有影响。
标签: ios iphone objective-c ipad core-data