【问题标题】:NSFetchedResultsController doesn't update UITableView after launchNSFetchedResultsController 在启动后不更新 UITableView
【发布时间】: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 文档报告它已打开之后。没有任何效果。任何帮助将不胜感激。

【问题讨论】:

    标签: iphone ios objective-c uitableview nsfetchedresultscontroller


    【解决方案1】:

    粗略浏览一下您的代码,我注意到您正在异步创建上下文。这很好,但是当上下文准备好时,您需要通知表视图控制器并让它调用 reloadData。

    数据存储中的完成处理程序需要在表视图控制器上设置上下文属性。

    【讨论】:

    • 我该怎么做。我确定这是需要在完成处理程序中进行的操作,但我不确定该怎么做。这听起来可能是问题所在。我做了一个测试,发现[fetchedObjects count]最初是0,然后在关闭模态VC后是5。
    【解决方案2】:

    NSFetchedResultsController 不会神奇地更新您的 tableView。我发现 Photomania 有点复杂,因为像 CoreDataViewController 这样的辅助类很少。因此 tableVew 只能通过调用 reloadData 方法或使用一对 beginUpdates-endUpdates 重新加载。而NSFetchedResultsController 只能通过调用performFetch: 方法来更新。 我一般用NSManagedObjectContextgetter:

    - (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
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-06
    • 1970-01-01
    • 2019-08-14
    • 1970-01-01
    • 2011-10-18
    • 2011-02-01
    相关资源
    最近更新 更多