NSFetchedResultsControllerDelegate を使う

2010年7月25日日曜日 | Published in | 0 コメント

このエントリーをはてなブックマークに追加

[前回] Cocoaの日々: NSFetchedResultsController のおさらい

今回は NSFetchedResultsControllerDelegate について調べた。


NSFetchedResultsControllerDelegate


NSFetchedResultsControllerDelegate Protocol Reference

NSFetchedResultsControllerDelegate は NSFetchedResultsController からのコールバックを受け取るためのメソッドが定義されているプロトコル。NSFetchedResutlsController は NSManagedObjectContext に対する操作(追加・変更・削除)を監視していて、それらを検出するとこのプロトコルのメソッドを呼び出す。この辺りは前回の Cocoaの日々: NSFetchedResultsController のおさらい にて少し触れた。

これらのメソッドの使い方は上記リファレンスの Overviewに書かれている。また Xcodeで Core Data を使うプロジェクトを作成すると、これらの実装コードが自動的に生成される。


利用パターン


利用パターンは3つ。

[A] 操作毎に処理するパターン


このパターンは NSManagedObjectContext の変更毎に呼び出されるメソッドを実装し、処理を行うパターン。Overviewではこれらが "Typical Use"として紹介されている。Xcodeが生成するコードもこのパターンになっている。

具体的には次のメソッドを実装する。
– controllerWillChangeContent:
– controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:
– controller:didChangeSection:atIndex:forChangeType:
– controllerDidChangeContent:

通常はこれらの処理で UITableView に対する操作を行う。以下、controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: の実装例の引用:
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
    atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
    newIndexPath:(NSIndexPath *)newIndexPath {
 
    UITableView *tableView = self.tableView;
 
    switch(type) {
 
        case NSFetchedResultsChangeInsert:
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
                       withRowAnimation:UITableViewRowAnimationFade];
            break;
 
        case NSFetchedResultsChangeDelete:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                       withRowAnimation:UITableViewRowAnimationFade];
            break;
 
        case NSFetchedResultsChangeUpdate:
            [self configureCell:[tableView cellForRowAtIndexPath:indexPath]
                  atIndexPath:indexPath];
            break;
 
        case NSFetchedResultsChangeMove:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                       withRowAnimation:UITableViewRowAnimationFade];
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
                       withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}

操作毎に UITableView のアニメーション付きで表示更新を行うと、ユーザには一件づつ処理が行われているのが視覚的にわかるようになる。


[B] 操作終了時のみ処理するパターン


controllerDidChangeContent: のみ実装するパターン。これは処理対象の件数が多く [A]のパターンではパフォーマンス的に問題が出る場合に採用する。例えば100件のデータを削除するなど。1件づつ視覚的フィードバックを行うと非常に時間がかかるため現実的ではない。この場合は操作最後に UITableView の全件読み直しを行う方法が取れる。

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


[C] デリゲートを利用しないパターン


NSFetchedResultsController.delegate = nil とするパターン。この場合は UITableView と NSManagedObjectContextとの同期を自前で処理する。


- - - -
通常は [A] を使い、処理件数が多い場合のみ [B] を使うといったハイブリッドなパターンも考えられる。


サンプルプログラム


動作確認するために複数の行を選択・削除できるサンプルプログラムを作ってみた。

[ソース] FetchedResultsControllerSample at 2010-07-25 from xcatsan's iOS-Sample-Code - GitHub



行を複数選択して、右下のボタンを押すと選択された行がアニメーションしながら表から消える。

サンプルコードのベースは、Xcodeの "Navigation-based Application" + "Use Core Data for storage" を使った。モデルの定義と調整、行の複数選択以外は手を入れていない。NSFetchedResultsControllerDelegate の実装メソッドは Xcodeが生成した雛形をそのまま利用した。つまり [A]パターンの動作となる。これだけで最低限の処理ができるので便利だ。


なお試しに NSFetchedResultsControllerDelegate の実装メソッドを controllerDidChangeContent: だけにしてみた([B]パターン)。この場合は [A]パターンと違いアニメーションは起こらず、画面全体が読み直される。


参考情報

Table View Programming Guide for iOS: Inserting and Deleting Rows and Sections
"Batch Insertion, Deletion, and Reloading of Rows and Sections" UITableViewのバッチ操作についての解説。

Responses

Leave a Response

人気の投稿(過去 30日間)