71 votes

NSFetchedResultsController avec des sections créées par la première lettre d’une chaîne

L'apprentissage de Base de Données sur l'iPhone. Il semble y avoir peu d'exemples sur la Base de Données de remplissage d'une vue de la table avec les sections. Le CoreDataBooks exemple utilise des parties, mais elles sont générées à partir de la pleine chaînes dans le modèle. Je veux organiser la Base de Données de la table en sections par la première lettre d'un nom de famille, à la manière du Carnet d'Adresses.

Je pouvais entrer et créer un autre attribut, c'est à dire une seule lettre, pour chaque personne, afin d'agir comme la section de la division, mais cela semble encombrants.

Voici ce que je suis en commençant par ... le truc semble être tromper l' sectionNameKeyPath:

- (NSFetchedResultsController *)fetchedResultsController {
//.........SOME STUFF DELETED
    // Edit the sort key as appropriate.
    NSSortDescriptor *orderDescriptor = [[NSSortDescriptor alloc] initWithKey:@"personName" ascending:YES];
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:orderDescriptor, nil];

    [fetchRequest setSortDescriptors:sortDescriptors];
    // Edit the section name key path and cache name if appropriate.
    // nil for section name key path means "no sections".
    NSFetchedResultsController *aFetchedResultsController = 
            [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest 
            managedObjectContext:managedObjectContext 
            sectionNameKeyPath:@"personName" cacheName:@"Root"];
//....
}

62voto

Greg Combs Points 2296

Dave DeLong l'approche est bonne, au moins dans mon cas, aussi longtemps que vous omettez un couple de choses. Voici comment cela fonctionne pour moi:

  • Ajouter un nouvel attribut string facultatif à l'entité appelée "lastNameInitial" (ou quelque chose de cet effet).

    Font de cette propriété transitoire. Cette signifie que les Données de Base ne dérange pas l'enregistrer dans votre fichier de données. Cette propriété n'existe pas dans la mémoire, quand vous en avez besoin.

    Générer les fichiers de classe pour cette de l'entité.

    Ne vous inquiétez pas au sujet d'un setter pour cette de la propriété. Créer cette getter (c'est la moitié de la magie, à mon humble avis)


// THIS ATTRIBUTE GETTER GOES IN YOUR OBJECT MODEL
- (NSString *) committeeNameInitial {
    [self willAccessValueForKey:@"committeeNameInitial"];
    NSString * initial = [[self committeeName] substringToIndex:1];
    [self didAccessValueForKey:@"committeeNameInitial"];
    return initial;
}


// THIS GOES IN YOUR fetchedResultsController: METHOD
// Edit the sort key as appropriate.
NSSortDescriptor *nameInitialSortOrder = [[NSSortDescriptor alloc] 
        initWithKey:@"committeeName" ascending:YES];

[fetchRequest setSortDescriptors:[NSArray arrayWithObject:nameInitialSortOrder]];

NSFetchedResultsController *aFetchedResultsController = 
        [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest 
        managedObjectContext:managedObjectContext 
        sectionNameKeyPath:@"committeeNameInitial" cacheName:@"Root"];


PRÉCÉDEMMENT: à la Suite de Dave premières étapes à la lettre généré des problèmes où il meurt sur setPropertiesToFetch avec un argument non valide exception. J'ai connecté le code et les informations de débogage ci-dessous:

NSDictionary * entityProperties = [entity propertiesByName];
NSPropertyDescription * nameInitialProperty = [entityProperties objectForKey:@"committeeNameInitial"];
NSArray * tempPropertyArray = [NSArray arrayWithObject:nameInitialProperty];

//  NSARRAY * tempPropertyArray RETURNS:
//    <CFArray 0xf54090 [0x30307a00]>{type = immutable, count = 1, values = (
//    0 : (<NSAttributeDescription: 0xf2df80>), 
//    name committeeNameInitial, isOptional 1, isTransient 1,
//    entity CommitteeObj, renamingIdentifier committeeNameInitial, 
//    validation predicates (), warnings (), versionHashModifier (null), 
//    attributeType 700 , attributeValueClassName NSString, defaultValue (null)
//    )}

//  NSInvalidArgumentException AT THIS LINE vvvv
[fetchRequest setPropertiesToFetch:tempPropertyArray];

//  *** Terminating app due to uncaught exception 'NSInvalidArgumentException',
//    reason: 'Invalid property (<NSAttributeDescription: 0xf2dfb0>), 
//    name committeeNameInitial, isOptional 1, isTransient 1, entity CommitteeObj, 
//    renamingIdentifier committeeNameInitial, 
//    validation predicates (), warnings (), 
//    versionHashModifier (null), 
//    attributeType 700 , attributeValueClassName NSString, 
//    defaultValue (null) passed to setPropertiesToFetch: (property is transient)'

[fetchRequest setReturnsDistinctResults:YES];

NSSortDescriptor * nameInitialSortOrder = [[[NSSortDescriptor alloc]
    initWithKey:@"committeeNameInitial" ascending:YES] autorelease];

[fetchRequest setSortDescriptors:[NSArray arrayWithObject:nameInitialSortOrder]];

NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] 
    initWithFetchRequest:fetchRequest 
    managedObjectContext:managedObjectContext 
    sectionNameKeyPath:@"committeeNameInitial" cacheName:@"Root"];

55voto

Greg Combs Points 2296

Je pense que j’ai encore une autre option, celui-ci utilise une catégorie sur NSString...

Maintenant un peu plus tard sur, tout en construisant votre CEAF :

C’est maintenant mon approche préférée. Beaucoup plus propre/plus facile à implémenter. En outre, vous n’avez pas d’apporter des modifications à votre classe de modèle objet pour le soutenir. Cela veut dire que ça va marcher sur n’importe quel modèle d’objet fourni à la section points de nom d’une propriété basée sur NSString

15voto

Dave DeLong Points 156978

Voici comment vous pourriez le faire fonctionner:

  • Ajouter une nouvelle option de la chaîne d'attribut de l'entité appelée "lastNameInitial" (ou quelque chose à cet effet).
  • Font de cette propriété transitoire. Cela signifie que les Données de Base ne vais pas la peine de l'enregistrer dans votre fichier de données. Cette propriété n'existe pas dans la mémoire, quand vous en avez besoin.
  • Générer les fichiers de classe pour cette entité.
  • Ne vous inquiétez pas au sujet d'un setter pour cette propriété. Créer cette getter (c'est la moitié de la magie, à mon humble avis)

    - (NSString *) lastNameInitial {
    [auto willAccessValueForKey:@"lastNameInitial"];
    NSString * initiale = [[auto lastName] substringToIndex:1];
    [auto didAccessValueForKey:@"lastNameInitial"];
    de retour initiale;
    }
  • Dans votre demande de récupération, demande SEULEMENT ce PropertyDescription, comme si (ce qui est un autre quart de la magie):

    NSDictionary * entityProperties = [myEntityDescription propertiesByName];
    NSPropertyDescription * lastNameInitialProperty = [entityProperties objectForKey:@"lastNameInitial"];
    [fetchRequest setPropertiesToFetch:[NSArray arrayWithObject:lastNameInitialProperty]];
  • Assurez-vous que votre demande de récupération ne retourne que des résultats distincts (c'est le dernier trimestre de la magie):

    [fetchRequest setReturnsDistinctResults:YES];
  • Trier vos résultats par cette lettre:

    NSSortDescriptor * lastNameInitialSortOrder = [[[NSSortDescriptor alloc] initWithKey:@"lastNameInitial" croissant:OUI] autorelease];
    [fetchRequest setSortDescriptors:[NSArray arrayWithObject:lastNameInitialSortOrder]];
  • l'exécution de la demande, et voir ce qu'il vous donne.

Si j'ai bien compris comment cela fonctionne, alors j'imagine qu'il va retourner un tableau de NSManagedObjects, chacun de qui a seulement le lastNameInitial bien chargé en mémoire, et qui sont un ensemble de différentes prénom initiales.

Bonne chance, et de faire rapport sur la façon dont cela fonctionne. Je viens de faire ce jusqu'au sommet de ma tête et que vous voulez savoir si cela fonctionne. =)

8voto

fawsha1 Points 336

J’aime Greg peignes réponse ci-dessus. J’ai fait une légère modification afin que les chaînes telles que « Smith » et « smith » peuvent apparaître dans la même section en convertissant les chaînes en majuscules :

6voto

Mike Baldus Points 41

Je rencontre ce problème tout le temps. La solution qui vous semble la meilleure que je reviens toujours à l'est de donner à l'entreprise un véritable première initiale de la propriété. Être un véritable champ fournit une plus grande efficacité de la recherche et de commande que vous pouvez définir le champ indexé. Il ne semble pas comme c'est trop de travail pour tirer la première initiale et remplir un second champ avec elle lors de la première importation des données / créé. Vous devez écrire cette première analyse de code, mais vous pourriez le faire une fois par entité et jamais plus. Les inconvénients semblent être que vous stockez un caractère supplémentaire par entité (et de l'indexation) vraiment, c'est probablement négligeable.

Une autre remarque. Je peur de la modification de l'généré entité code. Peut-être que je manque quelque chose, mais les outils pour générer des CoreData, les entités n'ont aucun respect pour le code, j'aurais pu. L'option-je prendre lors de la génération du code supprime toutes les personnalisations je l'aurais fait. Si je rempli mon entités avec des petites fonctions, puis-je besoin d'ajouter un tas de propriétés de l'entité, je ne peut pas se régénérer facilement.

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