【问题标题】:Refresh NSFetchedResultsController data?刷新 NSFetchedResultsController 数据?
【发布时间】:2011-12-12 01:12:56
【问题描述】:

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

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

我没有编辑表格视图。

只有一个托管上下文。

我无法理解我做错了什么。如果我关闭应用程序并重新启动我的新对象在表格视图中。当我收到新对象已生成的通知时,我尝试调用 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;
}

我这里有标准的 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...

【问题讨论】:

    标签: ios core-data reload


    【解决方案1】:

    您将UITableViewController 设置为NSFetchedResultsControllerDelegate。那挺好的。现在尝试在 TableViewController 中实现controllerDidChangeContent: 方法,如下所示:

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

    您的NSFetchedResultsController 将侦听Core Data 中已删除或新的对象,并通知其委托(您的TableViewController)更改。检查 Xcode 中的 Core Data 模板项目,通过 UITableView 中的添加和删除动画更好地实现这一点。

    【讨论】:

    • 当然,您需要重新加载 _notesTableView。
    • 也不要忘记NSFetchedResultsController 只会对-save:NSManagedObjectContext 上被调用作出反应。因此,如果您不保存新创建的实体,您可能不会看到此方法触发。
    • 谢谢,我正在从 NSFetchedResultsControllerDelegate 协议实现 controllerDidChangeContent:,在我的 tableview 上调用 reloadData。我添加了断点并且正在调用此方法,但是 tableview 重新加载为空。如果我重新启动应用程序,我可以看到我的所有记录,包括新记录。我错过了什么? PS:我是否必须实现控制器:didChangeObject:atIndexPath:forChangeType:newIndexPath?
    • @MarcusS.Zarra 似乎这是在我创建托管对象时触发的,而不是在我保存时触发的,这很痛苦,因为我正在创建我的对象并向其动态添加数据,这导致崩溃,因为 tableview 需要加载信息以显示在单元格中,然后再将此信息附加到对象
    • 您必须在上下文更改后重新获取。所以回到使用 NSFetchedResultsController,你会这样做:- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { [self.fetchedResultsController performFetch:&amp;error]; [self.tableView reloadData]; }
    【解决方案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"];
    }
    

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

        如果有人遇到同样的问题,就像我刚刚遇到的那样。我已经用绿色勾号尝试了上述解决方案,但它对我不起作用。我关注了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()
                }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-06-11
          • 2023-03-15
          • 1970-01-01
          • 2011-03-24
          • 2012-08-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多