64 votes

Comment implémenter la réorganisation des enregistrements CoreData?

Je suis à l'aide de CoreData pour mon iPhone, mais CoreData ne fournit pas un moyen automatique de la vous permettant de réorganiser les dossiers. J'ai pensé à utiliser une autre colonne pour stocker les informations sur la commande, mais à l'aide de numéros contigus pour la commande de l'indice a un problème. si je fais affaire avec beaucoup de données, la réorganisation d'un enregistrement implique potentiellement la mise à jour d'un lot de documents sur la commande info (c'est sorta comme une modification de l'ordre d'un élément de tableau)

Quelle est la meilleure façon de mettre en œuvre efficace de la commande de régime?

91voto

Aleksandar Vacic Points 1906

FetchedResultsController et de son délégué ne sont pas destinés à être utilisés pour l'utilisateur des changements de modèle. Voir la Pomme de référence doc. Recherchez axées sur l'Utilisateur, des Mises à jour de la partie. Donc, si vous cherchez de l'magique, une ligne de chemin, il n'y a pas, malheureusement.

Ce dont vous avez besoin, c'est de faire des mises à jour dans cette méthode:

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
 userDrivenDataModelChange = YES;

 ...[UPDATE THE MODEL then SAVE CONTEXT]...

 userDrivenDataModelChange = NO;
}

et aussi de prévenir les notifications à faire n'importe quoi, que des changements sont déjà effectuées par l'utilisateur:

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
 if (userDrivenDataModelChange) return;
 ...
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
 if (userDrivenDataModelChange) return;
 ...
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
 if (userDrivenDataModelChange) return;
 ...
}

J'ai juste mis cette dans mon application (Quickie) et il fonctionne très bien.

14voto

iWasRobbed Points 26926

Voici un exemple rapide montrant un moyen de vider les résultats récupérés dans un NSMutableArray qui vous permet de déplacer les cellules autour de. Alors que vous venez de mettre à jour un attribut dans l'entité appelée orderInTable puis enregistrer la gestion du contexte de l'objet.

De cette façon, vous n'avez pas à vous soucier de changer manuellement les indices et, au lieu de vous laisser le NSMutableArray gérer pour vous.

Créer une valeur BOOLÉENNE que vous pouvez utiliser pour désactiver temporairement l' NSFetchedResultsControllerDelegate

@interface PlaylistViewController ()
{
    BOOL changingPlaylistOrder;
}
@end

La vue de la Table délégué de la méthode:

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
{
    // Refer to https://developer.apple.com/library/ios/documentation/CoreData/Reference/NSFetchedResultsControllerDelegate_Protocol/Reference/Reference.html#//apple_ref/doc/uid/TP40008228-CH1-SW14

    // Bypass the delegates temporarily
    changingPlaylistOrder = YES;

    // Get a handle to the playlist we're moving
    NSMutableArray *sortedPlaylists = [NSMutableArray arrayWithArray:[self.fetchedResultsController fetchedObjects]];

    // Get a handle to the call we're moving
    Playlist *playlistWeAreMoving = [sortedPlaylists objectAtIndex:sourceIndexPath.row];

    // Remove the call from it's current position
    [sortedPlaylists removeObjectAtIndex:sourceIndexPath.row];

    // Insert it at it's new position
    [sortedPlaylists insertObject:playlistWeAreMoving atIndex:destinationIndexPath.row];

    // Update the order of them all according to their index in the mutable array
    [sortedPlaylists enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        Playlist *zePlaylist = (Playlist *)obj;
        zePlaylist.orderInTable = [NSNumber numberWithInt:idx];
    }];

    // Save the managed object context
    [commonContext save];

    // Allow the delegates to work now
    changingPlaylistOrder = NO;
}

Vos délégués ressemblerait à quelque chose comme ceci maintenant:

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
       atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
      newIndexPath:(NSIndexPath *)newIndexPath
{
    if (changingPlaylistOrder) return;

    switch(type)
    {
        case NSFetchedResultsChangeMove:
            [self configureCell:(PlaylistCell *)[self.tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;

    }
}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
    if (changingPlaylistOrder) return;

    [self.tableView reloadData];
}

8voto

dk. Points 862

Un retard de réponse: peut-être vous pouvez stocker la clé de tri comme une chaîne de caractères. L'insertion d'un enregistrement entre deux lignes existantes peut être fait trivialement par l'ajout d'un caractère supplémentaire à une chaîne, par exemple, l'insertion de "AM" entre les lignes "A" et "B". Pas de réorganisation est nécessaire. Une idée similaire pourrait être accompli à l'aide d'un nombre à virgule flottante ou une simple peu arithmétique sur un entier de 4 octets: insérer une ligne avec une valeur de clé de tri qui est à mi-chemin entre les lignes adjacentes.

Cas pathologiques pourraient surgir si la chaîne est trop longue, le flotteur est trop petit, ou il n'y a plus de place dans l'int, mais ensuite, vous pouvez simplement renuméroter l'entité et de prendre un nouveau départ. Une analyse à travers et mise à jour de tous vos enregistrements sur une occasion rare est beaucoup mieux que défaillant chaque objet, chaque fois qu'un utilisateur se réorganise.

Considérons, par exemple, int32. À l'aide de la haute 3 octets comme la première commande vous donne près de 17 millions de lignes avec la possibilité d'insérer jusqu'à 256 lignes entre deux lignes. 2 octets permet l'insertion de 65000 lignes entre deux lignes avant de les analyser.

Voici le pseudo-code que j'ai en tête pour un 2 octets d'incrémentation et de 2 octets pour l'insertion:

AppendRow:item
    item.sortKey = tail.sortKey + 0x10000

InsertRow:item betweenRow:a andNextRow:b
    item.sortKey = a.sortKey + (b.sortKey - a.sortKey) >> 1

Normalement, vous serait d'appeler AppendRow résultant dans les lignes avec sortKeys de 0x10000, 0x20000, 0x30000, etc. Parfois, vous devez InsertRow, dire entre la première et la deuxième, résultant en une sortKey de 0x180000.

5voto

Stephan Points 1274

J'ai implémenté l'approche de @andrew / @dk avec les valeurs doubles.

Vous pouvez trouver le UIOrderedTableView sur github.

n'hésitez pas à le fourrer :)

5voto

Arie Litovsky Points 2092

J'ai adapté cette méthode à partir du blog de Matt Gallagher (je ne trouve pas le lien original). Cela peut ne pas être la meilleure solution si vous avez des millions d'enregistrements, mais différera la sauvegarde jusqu'à ce que l'utilisateur ait terminé de réorganiser les enregistrements.

 - (void)moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath sortProperty:(NSString*)sortProperty
{
    NSMutableArray *allFRCObjects = [[self.frc fetchedObjects] mutableCopy];
    // Grab the item we're moving.
    NSManagedObject *sourceObject = [self.frc objectAtIndexPath:sourceIndexPath];

    // Remove the object we're moving from the array.
    [allFRCObjects removeObject:sourceObject];
    // Now re-insert it at the destination.
    [allFRCObjects insertObject:sourceObject atIndex:[destinationIndexPath row]];

    // All of the objects are now in their correct order. Update each
    // object's displayOrder field by iterating through the array.
    int i = 0;
    for (NSManagedObject *mo in allFRCObjects)
    {
        [mo setValue:[NSNumber numberWithInt:i++] forKey:sortProperty];
    }
    //DO NOT SAVE THE MANAGED OBJECT CONTEXT YET


}

- (void)setEditing:(BOOL)editing
{
    [super setEditing:editing];
    if(!editing)
        [self.managedObjectContext save:nil];
}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X