7

私はこの問題を数時間前から解決していますが、これまでのところ成功していません。私はここですべての質問を読んだので、それは私の問題に関連するかもしれません。述語が変更された後にNSFetchedResultsControllerの代理人が起動しない

私はこのような1対多の関係を経由してツリー状の構造で相互に接続されている2つのエンティティ(A、B)持っている:< --- >> B.

私はそのための述語を使用していたエンティティAの選択したオブジェクトに関連するエンティティBのすべてのオブジェクトを表示するにはNSFetchedResultsControllerに裏打ちされたのUITableViewControllerがあります:

- (NSFetchedResultsController *)fetchedResultsController 
{ 
if (_fetchedResultsController != nil) { 
    return _fetchedResultsController; 
} 

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; 
NSEntityDescription *entity = [NSEntityDescription entityForName:@"B" inManagedObjectContext:self.managedObjectContext]; 
[fetchRequest setEntity:entity]; 

[fetchRequest setFetchBatchSize:20]; 

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"controlDate" ascending:NO]; 
NSArray *sortDescriptors = @[sortDescriptor]; 
[fetchRequest setSortDescriptors:sortDescriptors]; 

NSMutableArray *subpredicates = [NSMutableArray array]; 
NSPredicate *predicate1 = [NSPredicate predicateWithFormat:@"isRelatedTo = %@", selectedObjectOfA]; 
[subpredicates addObject:predicate1]; 
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:@"syncStatus != %d", ObjectDeleted]; 
[subpredicates addObject:predicate2]; 

NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates]; 
[fetchRequest setPredicate:predicate]; 

NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil]; 
aFetchedResultsController.delegate = self; 
self.fetchedResultsController = aFetchedResultsController; 

NSError *error = nil; 
if (![self.fetchedResultsController performFetch:&error]) { 
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 
    abort(); 
} 

return _fetchedResultsController; 
} 

すべてが削除、追加するために正常に動作しますおよび編集オブジェクト。テーブルはそれに応じて更新されます。私のクラスはNSFetchedResultsControllerDelegateに準拠し、デリゲートメソッドは定型文です。

しかし、ユーザーは、ナビゲーションスタックに戻らずに「上」/「下」ボタンをタップすることで、エンティティAのすべてのオブジェクトを循環することができます。メールアプリ)。 BのrelatedObjectsの数は、選択されたAのオブジェクトのdepening(noneとandの間で変化します)。

そしてここで問題が来る: 私の意図は、述語を変更することにより、FRCの結果を更新することである(ない、それは構造や構文が、「selectedObjectOfA」の値だけだとすべての残り

[self.fetchedResultsController performFetch:&error]; 

を呼び出しますFRCによって行われるべき

最初の質問は次のようになります。?このアプローチは正しいです

ここで私はすでに試したシナリオです:

  • 10は、単に新しい値に「selectedObjectOfA」を設定し、(fetchRequestと述語が更新されていないので、私が思う)常に同じ結果を示している「リセット」

  • をperformFetchを呼び出すFRCのfetchrequestのようなこの

    NSMutableArray *subprediactes = [NSMutableArray array]; 
    NSPredicate *predicate1 = [NSPredicate predicateWithFormat:@"isRelatedTo = %@", self.selectedObjectOfA]; 
    [subprediactes addObject:predicate1]; 
    NSPredicate *predicate2 = [NSPredicate predicateWithFormat:@"syncStatus != %d", ObjectDeleted]; 
    [subprediactes addObject:predicate2]; 
    
    NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:subprediactes]; 
    [self.fetchedResultsController.fetchRequest setPredicate:predicate]; 
    

FRCを更新し、私が思うように正しいデータを返しますが、行

の不正確な数のアプリがクラッシュします
'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section before the update (5), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).' 

NSFetchedResultsControllerデリゲートメソッドが呼び出されないためです。したがって、tableViewの行数は新しい結果に適合しません(返されるオブジェクトの数はエラーメッセージに従って正しい)。

2番目の質問は、なぜオブジェクトを追加/削除するときにデリゲートが起動しますが、述語(したがって結果)が変更されていない場合ですか?

ありがとうございます。

答えて

3

あなたはフェッチ要求を変更した後

[tableView reloadData]; 

を呼び出す必要があります。取得された結果コントローラは、performFetchの後に発生した変更のみを追跡します。

あなたはテーブルビューのアニメーション更新を取得するには、次のを試みることができる(しかし、私はまだことを試していないので、私はそれが動作するかどうかわからない):

  • コール[tableView beginUpdates]を。
  • 「古い」行をすべて削除するには、[tableView deleteRowsAtIndexPaths:...]を使用します。
  • フェッチ結果コントローラのフェッチ要求を更新します。
  • [fetchedResultsController performFetch:...]を呼び出します。
  • [fetchedResultsController fetchedObjects]のすべてのオブジェクトに対して[tableView insertRowsAtIndexPaths:...]を呼び出して、すべての「新しい」行を挿入します。
  • [tableView endUpdates]を呼び出します。
+0

あなたが正しいです。私はこれもやってみました。残念ながら '[tableview beginUpdates]'と '[tableView endUpdates]'の行アニメーション(行の追加/削除)はこの場合失われます。それを保つ可能性はありますか? 述語を正しく変更して新しいフェッチを実行すると、FRCの変更の追跡が再開されます。つまり、(代理人が呼び出されないため)「スムーズ」な遷移はできません。あなたは確認できますか? – Francesco

+0

@FranzBernt:アニメーションテーブルビューの更新とともにFRCフェッチ要求を変更するための「ビルトイン」メソッドはありません。 - 私はこれを達成するための考え方を持っていました(更新された答えを見てください)が、それがうまくいくかどうか分かりません。 –

+0

@FranzBernt:更新方法が有効かどうかお知らせください。 (そうでなければ 'reloadData'を使わなければなりません)。 –

4

述語を変更した後、あなたのテーブルビューをアニメーション化する:

NSFetchRequest* fetchRequest = self.fetchedResultsController.fetchRequest; 
fetchRequest.predicate = newPredicate; 

NSArray* objectsBefore = self.fetchedResultsController.fetchedObjects; 

[self.fetchedResultsController performFetch:nil]; 

NSArray* objectsAfter = self.fetchedResultsController.fetchedObjects; 


[self.tableView beginUpdates]; 

if (objectsBefore.count > 0) 
{ 
    for (id objectBefore in objectsBefore) 
    { 
     if ([objectsAfter indexOfObject:objectBefore] == NSNotFound) 
     { 
      NSIndexPath* indexPath = [NSIndexPath indexPathForRow:[objectsBefore indexOfObject:objectBefore] inSection:0] ; 

      [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationMiddle]; 
     } 
    } 
} 

if (objectsAfter.count > 0) 
{ 
    for (id objectAfter in objectsAfter) 
    { 
     if ([objectsBefore indexOfObject:objectAfter] == NSNotFound) 
     { 
      NSIndexPath* indexPath = [NSIndexPath indexPathForRow:[objectsAfter indexOfObject:objectAfter] inSection:0]; 

      [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationMiddle]; 
     } 
    } 
} 

[self.tableView endUpdates]; 
+1

本当に私のために働いて、ありがとう – bitwit

関連する問題