2009-08-25 4 views
10

Les docs pour NSFetchedResultsControllerDelegate fournissent le code exemple suivantLe comportement 'ChangeUpdate' de NSFetchedResultsControllerDelegate est-il rompu?

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

    } 

} 

Lorsque je crée une nouvelle NSManagedObject, NSFetchedResultsChangeInsert feux (grands!). Lorsque je modifie la valeur d'un attribut (utilisé pour le titre de la cellule), les incendies NSFetchedResultsChangeUpdate. Malheureusement, le nouveau titre ne s'affiche pas automatiquement à moins de recharger le tableau, la section ou la rangée. En effet, si le nouveau nom provoque un tri différent du jeu de résultats, alors NSFetchedResultsChangeMove se déclenche et tout va bien puisque le code fourni recharge toute la section.

UITableView a une méthode reloadRowsAtIndexPaths: withRowAnimation donc j'essayé d'utiliser ce sous le bloc de code NSFetchedResultsChangeUpdate. Il ne fonctionne en effet ... mais la documentation de cette méthode spécifique lu comme si je ne pas besoin (notez la dernière ligne):

Rechargement une ligne provoque l'affichage du tableau pour demander sa source de données pour une nouvelle cellule pour cette ligne. La table anime cette nouvelle cellule alors qu'elle anime l'ancienne ligne. Appelez cette méthode si vous souhaitez alerter l'utilisateur que la valeur d'une cellule est en train de changer. Si, cependant, notifiant l'utilisateur n'est pas important-c'est-à-dire, vous voulez juste changer la valeur qu'une cellule est affichage-vous pouvez obtenir la cellule pour une ligne particulière et définir sa nouvelle valeur.

Et oui, si je me connecte ce qui se passe, quand

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

obtient invoquaient sur une NSFetchedResultsChangeUpdate, il est en mesure de récupérer la dernière valeur « nom » et le mettre dans Textlabel de la cellule . Le nom n'est simplement rendu dans la cellule que si je le recharge. Même si je clique simplement sur la cellule, le nom apparaît. Notez que pour recréer ce comportement, vous devez créer un nouvel objet géré et ensuite lui donner un nom qui l'amène à trier FIRST dans le NSFetchedResultsController. De cette façon, le NSFetchedResultsChangeMove ne se déclenche pas (ce qui fonctionne car il recharge la section).

Ai-je manqué quelque chose ou est ce comportement attendu? La 'discussion' pour reloadRowsAtIndexPaths m'amène à croire que je devrais être capable de simplement définir textLabel de la cellule sans recharger la ligne, section ou table.

+0

Cela devrait être rking. Je fais la même chose. Pour les modifications apportées aux attributs, lorsque save: est appelé, les notifications MOC disparaissent. NSFetchedResultsController voit la modification et appelle ma méthode configureCell: atIndexPath. Là, j'attrape les valeurs et remplis la cellule, et la cellule est mise à jour immédiatement. Postez votre code configureCell. –

Répondre

3

Vous devez appeler [cell setNeedsLayout] ou/et [cell setNeedsDisplay] pour que la cellule soit actualisée, en fonction de l'implémentation de votre cellule.

Si vous composez la cellule de sous-vues comme nous le faisons habituellement, vous comptez sur - layoutSubviews, vous devriez donc appeler [cell setNeedsLayout].

Si vous dessinez la cellule directement avec – drawRect:, vous devez appeler [cell setNeedsDisplay].

Si vous utilisez à la fois la composition et le dessin, vous devez appeler les deux.

1

Même s'il est vrai que vous n'avez pas besoin de recharger la cellule pour que les modifications aient lieu, vous devez vous rappeler que l'iPhone met en cache le dessin autant que possible. Une fois que vous avez nouvellement configuré votre cellule, vous devez appeler setNeedsDisplay sur la cellule afin de déclencher le redraw.

+0

À ce stade - quel est le meilleur moyen d'obtenir la cellule qui est à l'écran? tableview: cellForRowAtIndexPath: crée une nouvelle cellule alors que je veux obtenir une référence à la cellule à l'écran ... non? –

+0

Normalement, il n'y a pas besoin, pour toute cellule que vous configurez, vous pouvez appeler setNeedsDisplay. Si c'est une nouvelle cellule, il n'y a pas de cache, donc pas de soucis, si vous reconfigurez, il sera redessiné. Si vous voulez vraiment savoir quels écrans sont visibles, cependant, jetez un oeil à ma réponse à: http://stackoverflow.com/questions/996515/getting-visible-cell-from-uitableview-pagingenabled/1566432#1566432 –

1

Bien qu'il soit pas explicitement indiqué, vous avez bien vous assurer que vous incluez les méthodes de délégués pour controllerWillChangeContent: et controllerDidChangeContent:

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

et

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

Le NSFRC peut tirer off contrôleur multiple: didChangeObject: atIndexPath: forChangeType: newIndexPath: méthodes au cours d'une modification, ne vous attendez donc pas à ce que la ligne du tableau soit mise à jour directement après chacune d'entre elles. Ils ne seront mis à jour qu'après l'appel de endUpdates sur la table.

0

appel à la logique de mise à jour l'interface utilisateur dans le thread principal résolu mon problème (les deux [cell setNeedsLayout] & [cell setNeedsDisplay] pas fonctionne pour moi):

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

Qui plus (pas sur cette question, mais il sera utile), vous feriez mieux d'utiliser le newIndexPath s'il est disponible:

... 
case NSFetchedResultsChangeUpdate: 
    dispatch_async(dispatch_get_main_queue(), { 
     NSIndexPath * targetIndexPath = (newIndexPath ?: indexPath) 
     [self configureCell:[tableView cellForRowAtIndexPath:targetIndexPath] 
       atIndexPath:targetIndexPath]; 
    }); 
    break; 
... 
+1

les événements sont déjà sur le thread principal, ce que vous faites est de passer à la prochaine boucle d'événement, bien que GCD ne soit pas à 100% bon à ce niveau, performer après le délai est plus fiable. – malhal

+0

@malhal bon point. – Kjuly

Questions connexes