web-dev-qa-db-ja.com

NSFetchedResultsControllerデータを更新しますか?

アプリ内に新しい管理対象オブジェクトを作成できます。別のビューに、所有しているすべてのオブジェクトを表示するテーブルビューがあります。

(完全に別のビューで)新しい管理対象オブジェクトを作成すると、作成したばかりの新しいオブジェクトを含む新しいデータをテーブルビューに再読み込みしようとする通知を送信します。

テーブルビューを編集していません。

管理対象コンテキストは1つだけです。

自分が間違っていることを理解できません。アプリを閉じて再起動すると、新しいオブジェクトがテーブルビューに表示されます。新しいオブジェクトが作成されたという通知を受け取ったときに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;
    }
}

新しいオブジェクトがCoreDataに追加されたことを通知する通知を待っています。次に、上記のメソッドを呼び出してから、テーブルビューでreloadDataを呼び出しています...

14
Daniel

UITableViewControllerNSFetchedResultsControllerDelegateとして設定しています。それは良い。次に、次のようにTableViewControllerにcontrollerDidChangeContent:メソッドを実装してみます。

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

NSFetchedResultsControllerは、Core Data内の削除されたオブジェクトまたは新しいオブジェクトをリッスンし、そのデリゲート(TableViewController)に変更を通知します。 XcodeのCoreDataテンプレートプロジェクトをチェックして、UITableViewの追加および削除アニメーションでこれをさらに適切に実装します。

27
huesforalice

必ず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"];
}

この後、新しい管理対象オブジェクトがテーブルビューに表示されました。

2
codiac

最新のSwift 4.2およびXcode10に更新されました。

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()
    }
}
0
Phuah Yee Keat

私がちょうどそれを持っていたのと同じ問題を抱えている人の場合。上記の解決策を緑色のチェックマークで試しましたが、うまくいきません。私は Appleのコード をフォローしましたが、すべてうまくいきました。

単純に、3つの「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()
        }
0
ofcourse_swift