ひとりまとめ

もろもろのメモ

NSFetchedResultsControllerを使ってTableViewを表示したい!

 

MagicalRecordもそれっぽく使えるようになって簡単にDB操作ができるようになったことだし、あらためてTableViewでデータを表示しようと思ってみた。

調べてみると、「NSFetchedResultsController」っていうのを使うといいらしい という情報を得た。どうにかこうにか表示までこぎ着けたのでメモ。

 

NSFetchedResultsControllerを使うと、どういいのか?

これを使うと、紐づけられたデータが更新された際に、表示を更新させたりするのが楽になるっぽげ。使っとこ。

 

Delegateを設定する

TableViewControllerで使うとすると、この.mファイルにDelegateをつけときます。

@interface RecordTableViewController () <NSFetchedResultsControllerDelegate> 

 

NSFetchedResultsController の 変数を用意

使いやすいように、同じくTableViewController.mにNSFetchedResultsControllerの変数を用意しておきます。これに表示するデータを入れます。

@property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController; 

 

 NSFetchedResultsControllerにデータを入れる

TableViewで表示したいデータを、用意しておいたfetchedResultsControllerという変数に入れます。このクラスの中で使いやすいように、その名も「fetchedResultsController」とすることが多いっぽげ。なお、ここでは覚えたてのMagicalRecordを使ってデータを取り出しています。(SSCatっていうのは、Entityに紐づいたクラスです)

- (NSFetchedResultsController*)fetchedResultsController

{
    if(_fetchedResultsController == nil)
    {
        self.fetchedResultsController = [SSCat fetchAllSortedBy:@"name" ascending:NO withPredicate:nil groupBy:nil delegate:self];
    }
   
    return _fetchedResultsController;

 

セルを描画するメソッドを用意する

NSFetchedResultsControllerは値を更新するのを監視して、変更があると再描画することが出来ます。そんなこんなで何カ所かセルの描画をするタイミングがあるため、メソッドとして分けておきます。(ベタに書くならナシでもOK。)

今回は、指定行(indexPath)のデータにある「name」というattitudeをラベルに表示しています。cellと指定行を渡してもらうので、もしもTableViewに独自のcellを使いたい時はここも合わせないといけません。

-(void)configureCell:(UITableViewCell*)cell atIndexPath:(NSIndexPath*)indexPath

{

    SSCat *cat = [_fetchedResultsController objectAtIndexPath:indexPath];
    cell.textLabel.text = cat.name;
 

デリゲート用のメソッドを書く

NSFetchedResultsControllerDelegateを使うので、デリゲートを書きます。
必要なのは、次のメソッド。
– controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:
 挿入・削除・更新・入れ替えの4パターンそれぞれ対応
 
– controller:didChangeSection:atIndex:forChangeType:
 セクションに変更があったとき
 
– controllerWillChangeContent:
 データの中身が変わる直前
 
 
– controllerDidChangeContent: 
 データの中身が変わった直後
 
まずはdidChangeObjectなやつから。switchで、4パターンそれぞれを記述してる。ここにあるNSFetchedResultsChangeUpdateで、先に定義しておいた「セル描画メソッド」を呼んでる。それ以外のは実は(?)アニメーション処理。

- (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;
    }

 

続いてdidChangeSectionの。こちらは挿入と削除に対応。

- (void)controller:(NSFetchedResultsController*)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
    switch(type)
    {
        case NSFetchedResultsChangeInsert:
            [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;
            
        case NSFetchedResultsChangeDelete:
            [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}

 

最後はcontrollerWillChangeContentとcontrollerDidChangeContent。特に何もなければTableViewに対してそれぞれのイベントを伝えてあげるだけで良いみたい。 

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

{
    [self.tableView endUpdates];

 
 

通常該当の行の内容を返すメソッドを書き換え

- tableView:cellForRowAtIndexPath:indexPath の中から、「セル描画メソッド」を呼ぶようにする。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
    }
    [self configureCell:cell atIndexPath:indexPath];
    return cell;

 


これでどうにかNSFetchedResultsControllerを使ってデータをTableViewに表示が出来ましたー!
やれやれ。
 

 レベルアップ という言葉に惹かれてる。

レベルアップ Objective-C (Software Design plus)

レベルアップ Objective-C (Software Design plus)