ios 刷新 NSFetchedResultsController 数据?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7840778/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 21:58:09  来源:igfitidea点击:

Refresh NSFetchedResultsController data?

ioscore-datareload

提问by Daniel

I can create new managed objects inside my app, in a separate view I have a table view which shows all the objects I have.

我可以在我的应用程序中创建新的托管对象,在一个单独的视图中,我有一个表视图,它显示了我拥有的所有对象。

When I create a new managed object (in a totally separate view), I am sending out a notification which is trying to get my table view to reload with the new data which includes the new object i just made.

当我创建一个新的托管对象(在一个完全独立的视图中)时,我发送了一个通知,它试图让我的表视图重新加载包含我刚刚创建的新对象的新数据。

I'm not editing the table view.

我不是在编辑表格视图。

There is only one managed context.

只有一个托管上下文。

I can't grasp what I am doing wrong. If I close the app and relaunch my new object is in the table view. I am trying to call reloadData when I get the notification a new object has been made, and I've also tried performing the fetch again, but all I end up with is an empty table view.

我无法理解我做错了什么。如果我关闭应用程序并重新启动我的新对象在表视图中。当我收到通知时,我试图调用 reloadData 一个新对象已经创建,我也尝试再次执行提取,但我最终得到的是一个空的表视图。

In terms of code, my ListViewController:

在代码方面,我的 ListViewController:

@interface MyNotesViewController : UIViewController
{
    NSManagedObjectContext * __managedObjectContext;
    UITableView * _notesTableView;
    MyNotesTableViewDelegate_DataSource * _tableDelegate_DataSource;
}

My viewDidLoad looks like this:

我的 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];
    }
}

In my delegate/datasource object I have:

在我的委托/数据源对象中,我有:

@interface MyCommutesTableViewDelegate_DataSource : NSObject 

<UITableViewDataSource, NSFetchedResultsControllerDelegate>
{
    NSManagedObjectContext * __managedObjectContext;
    NSFetchedResultsController * __fetchedResultsController;
}

@property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;

@end

Implementation excerpt:

实施摘录:

-(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;
}

I have the stander UITableView data source methods here:

我在这里有标准的 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;
}

UPDATE

更新

I reverted to simply requesting all objects because I had to quick fix my problem, and then I realised, after I save my context, my fetch request I had previously used on the app launch, now returns no data, there's no error but it just returns nothing.

我恢复到简单地请求所有对象,因为我必须快速解决我的问题,然后我意识到,在我保存我的上下文后,我之前在应用程序启动时使用的获取请求现在没有返回数据,没有错误,但它只是什么都不返回。

So I guess there's no issue with how I'm implementing NSFetchResultsController. But how could the request start not returning results after I save a new object ?

所以我想我如何实现 NSFetchResultsController 没有问题。但是,在我保存新对象后,请求怎么会开始不返回结果呢?

Note I still get all of my objects if I relaunch, including the newly added one.

请注意,如果我重新启动,我仍然可以获得所有对象,包括新添加的对象。

standard request:

标准要求:

-(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;
    }
}

I am waiting for a notification telling me a new object has been added to Core Data, and then I am calling the above method and then calling reloadData on my tableview...

我正在等待通知,告诉我一个新对象已添加到 Core Data,然后我调用上述方法,然后在我的 tableview 上调用 reloadData ...

回答by huesforalice

You are setting your UITableViewControlleras the NSFetchedResultsControllerDelegate. That's good. Now try to implement the controllerDidChangeContent:method in the TableViewController like so:

您正在将您设置UITableViewControllerNSFetchedResultsControllerDelegate. 那挺好的。现在尝试controllerDidChangeContent:在 TableViewController 中实现该方法,如下所示:

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
   [self.tableView reloadData];
}

Your NSFetchedResultsControllerwill listen to removed or new objects in Core Data and notify its delegate (your TableViewController) of changes. Check the Core Data template project in Xcode to implement this even better with add and removal animations in the UITableView.

NSFetchedResultsController将侦听 Core Data 中已删除或新的对象,并将更改通知其委托(您的 TableViewController)。检查 Xcode 中的 Core Data 模板项目,以通过UITableView.

回答by codiac

Be sure to add the didChangeObject method:

一定要添加 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"];
}

The new managed object showed up in the table view after this.

此后,新的托管对象出现在表视图中。

回答by Phuah Yee Keat

Updated to latest swift 4.2 and Xcode 10.

更新到最新的 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()
    }
}

回答by Ofcourse

In case someone having the same issue, as I just had it. I have tried the above solution with green tick, but it wouldn't work for me. I have followed Apple's codeand everything went fine.

如果有人遇到同样的问题,就像我刚刚遇到的那样。我已经用绿色勾号尝试了上述解决方案,但它对我不起作用。我遵循了Apple 的代码,一切顺利。

simply, just implement the three "Fetchcontroller" delegate functions

简单地说,只需实现三个“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()
        }