10

NSFetchedResultsControllerDelegate のドキュメントは、次のサンプル コードを提供します。

- (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 reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
            break;

    }

}

新しい NSManagedObject を作成すると、NSFetchedResultsChangeInsertが起動します (すばらしい!)。(セルのタイトルに使用される) 属性の値を変更すると、NSFetchedResultsChangeUpdateが発生します。残念ながら、テーブル、セクション、または行をリロードしない限り、新しいタイトルは自動的に表示されません。実際、新しい名前によって結果セットのソートが異なる場合、NSFetchedResultsChangeMoveが起動し、提供されたコードがセクション全体をリロードするため、すべて問題ありません。

UITableView にはメソッドreloadRowsAtIndexPaths:withRowAnimationがあるので、 NSFetchedResultsChangeUpdateコード ブロックでこれを使用してみました。それは確かに機能します...しかし、この特定のメソッドのドキュメントは、私がそれを必要としないかのように読みます(最後の行に注意してください):

行を再ロードすると、テーブル ビューはデータ ソースにその行の新しいセルを要求します。テーブルは、古い行をアニメーション化するのと同じように、その新しいセルをアニメーション化します。セルの値が変化していることをユーザーに警告する場合は、このメソッドを呼び出します。ただし、ユーザーへの通知が重要でない場合、つまり、セルに表示されている値を変更したいだけの場合は、特定の行のセルを取得して新しい値を設定できます。

そして、はい、何が起こっているかをログに記録すると、いつ

[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath]; 

NSFetchedResultsChangeUpdateで呼び出されると、最新の「name」値を取得してセルの textLabel に設定できます。リロードしない限り、セルに名前が表示されません。セルをクリックするだけでも、名前が表示されます。この動作を再現するには、新しい管理対象オブジェクトを作成し、NSFetchedResultsController で最初に並べ替える名前を付ける必要があることに注意してください。そうすれば、NSFetchedResultsChangeMove は起動しません (セクションをリロードするため、機能します)。

何か不足していますか、それともこれは予想される動作ですか? reloadRowsAtIndexPathsの「議論」により、行、セクション、またはテーブルをリロードせずに、セルの textLabel を単純に設定できるはずだと思うようになりました。

4

4 に答える 4

3

セルの実装に応じて、[cell setNeedsLayout]or/andを呼び出してセルを更新する必要があります。[cell setNeedsDisplay]

通常のようにサブビューのセルを作成する場合は、 に依存する- layoutSubviewsため、 を呼び出す必要があります[cell setNeedsLayout]

でセルを直接描画する場合は– drawRect:、 を呼び出す必要があります[cell setNeedsDisplay]

コンポジションと描画の両方を使用する場合は、両方を呼び出す必要があります。

于 2011-05-13T04:58:48.740 に答える
1

変更を有効にするためにセルをリロードする必要がないことは事実ですが、iPhone は可能な限りビューの描画をキャッシュすることを覚えておく必要があります。セルを新しく構成したら、再描画をトリガーするためにセルで setNeedsDisplay を呼び出す必要があります。

于 2009-10-12T14:38:34.963 に答える
1

明示的には述べられていませんが、実際には、controllerWillChangeContent: および controllerDidChangeContent: のデリゲート メソッドが含まれていることを確認する必要があります。

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

- (void)controllerDidChangeContent:(BSFetchedResultsController *)controller {
    @try {
        [self.tableView endUpdates];
    }
    @catch (NSException * e) {
        NSLog(@"caught exception: %@: %@", [e name], [e description]);
    }
    @finally { }
}

NSFRC は、1 つの変更中に複数の controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: メソッドを起動する可能性があるため、テーブル行がそれらの各変更の直後に更新されるとは思わないでください。テーブルで endUpdates を呼び出した後にのみ更新されます。

于 2011-02-09T13:13:53.080 に答える
0

メインスレッドでUI更新ロジックを呼び出すと、問題が解決し[cell setNeedsLayout]ました(両方とも[cell setNeedsDisplay]機能しません):

...
case NSFetchedResultsChangeUpdate:
    dispatch_async(dispatch_get_main_queue(), {
      [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
    });
    break;
...

さらに (この問題についてではありませんが、役に立つでしょう)、利用可能な場合は、それを使用することを選択することをお勧めしnewIndexPathます:

...
case NSFetchedResultsChangeUpdate:
    dispatch_async(dispatch_get_main_queue(), {
      NSIndexPath * targetIndexPath = (newIndexPath ?: indexPath)
      [self configureCell:[tableView cellForRowAtIndexPath:targetIndexPath]
              atIndexPath:targetIndexPath];
    });
    break;
...
于 2015-04-21T15:05:50.247 に答える