私はここで(少なくとも私にとっては)ストランの問題を抱えています:
表示するデータを処理するためのNSFetchedResultsControllerを持つUITableViewControllerがあります。
表示する必要のあるデータはサーバーからのものであり、簡単にするために、一種のチャットだとしましょう。
簡略化されたモデルは次のように説明できます。
--------------- ----------------- -----------------
| MainTopic | | Thread | | Message |
--------------- ----------------- -----------------
| topicID (E) | | threadID (E) | | msgID (E) |
| name (E) | | name (E) | | msgText (E) |
--------------- ----------------- | msgDate (E) |
| threads (R) | <------>> | mainTopic (R) | -----------------
--------------- | messages (R) | <------>> | thread (R) |
----------------- -----------------
データは、MainTopicのすべてのメッセージを常に入力するサーバーから取得されます(これは私の手にはありません)。
CoreDataの場合、シングルトンとして実装されているコントローラーがあります。NSFetchedResultsControllerによって使用されるmainContext(NSManagedObjectContext)があります。新しいデータの挿入は、保存時にmainContextでmergeChangesFromContextDidSaveNotification:を呼び出す別のNSManagedObjectContextで行われます。
Threadクラスにはcompare:-メソッドがあるため、sortdescriptorにも使用できます。
UITableViewControllerに戻ります。
viewDidLoadで、topicIDのすべてのメッセージを取得するNSFetchedResultsControllerを作成し、sectionNameKeyPathを「thread」に設定してFetchedResultsControllerでフェッチを実行します。
UITableViewはNSFetchedResultsControllerDelegateも実装しているため、新しいメッセージが到着した場合にTableViewが更新されます。そして、これが私の問題の始まりです。
サーバーからの新規フェッチ後、テーブルビューは次のようになります。
______________________________________
| Thread: First thread |
|____________________________________|
|____________________________________|
| Message 1 |
| Message 2 |
|____________________________________|
| Thread: Second thread |
|____________________________________|
|____________________________________|
| Message 1 |
| Message 2 |
|____________________________________|
ここで、誰かが最初のスレッドにメッセージ(メッセージ3)を書き込み、データがサーバーからフェッチされます(ユーザーはテーブルビューに残ります)。これで、テーブルビューは次のようになります
______________________________________
| Thread: First thread |
|____________________________________|
|____________________________________|
| Message 1 |
| Message 2 |
|____________________________________|
| Thread: First thread |
|____________________________________|
|____________________________________|
| Message 3 |
|____________________________________|
| Thread: Second thread |
|____________________________________|
|____________________________________|
| Message 1 |
| Message 2 |
|____________________________________|
ご覧のとおり、最初のスレッドのセクションヘッダーが2回表示されます。
UITableViewControllerを残して再度開くと(ポップされ、新しいインスタンスがnavigationControllerにプッシュされる)、テーブルは正常に表示されます。
______________________________________
| Thread: First thread |
|____________________________________|
|____________________________________|
| Message 1 |
| Message 2 |
| Message 3 |
|____________________________________|
| Thread: Second thread |
|____________________________________|
|____________________________________|
| Message 1 |
| Message 2 |
|____________________________________|
したがって、エラーはNSFetchedResultsControllerDelegateメソッドのどこかにあるはずですが、それらは「正常」に見えます。
#pragma mark - NSFetchedResultsControllerDelegate methods
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[[self tableView] beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller
didChangeSection:(id<NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex
forChangeType:(NSFetchedResultsChangeType)type
{
NSIndexSet * indexSet = [NSIndexSet indexSetWithIndex:sectionIndex];
switch (type) {
case NSFetchedResultsChangeInsert:
[[self tableView] insertSections:indexSet
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[[self tableView] deleteSections:indexSet
withRowAnimation:UITableViewRowAnimationFade];
break;
default:
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller
didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath
forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
NSArray * oldArray = nil;
if(indexPath) {
oldArray = [NSArray arrayWithObject:indexPath];
}
NSArray * newArray = nil;
if(newIndexPath) {
newArray = [NSArray arrayWithObject:newIndexPath];
}
switch(type) {
case NSFetchedResultsChangeInsert:
[[self tableView] insertRowsAtIndexPaths:newArray
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[[self tableView] deleteRowsAtIndexPaths:oldArray
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
{
UITableViewCell * cell = nil;
Message * message = nil;
cell = [[self tableView] cellForRowAtIndexPath:indexPath];
message = [[self fetchedResultsController] objectAtIndexPath:indexPath];
[cell setMessageText:[message msgText]];
}
break;
case NSFetchedResultsChangeMove:
[[self tableView] insertRowsAtIndexPaths:newArray
withRowAnimation:UITableViewRowAnimationFade];
[[self tableView] deleteRowsAtIndexPaths:oldArray
withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[[self tableView] endUpdates];
}
それで、誰かが私にヒントを持っていますか、問題はどこにあるのでしょうか?