Kerndaten auf dem iPhone lernen. Es scheint nur wenige Beispiele für Kerndaten zu geben, die eine Tabellenansicht mit Abschnitten füllen. Im CoreDataBooks- Beispiel werden Abschnitte verwendet, die jedoch aus vollständigen Zeichenfolgen im Modell generiert werden. Ich möchte die Kerndatentabelle nach dem Anfangsbuchstaben eines Nachnamens, a la Adressbuch, in Abschnitte unterteilen.
Ich könnte hineingehen und für jede Person ein anderes Attribut erstellen, dh einen einzelnen Buchstaben, um als Abteilungsabteilung zu fungieren, aber das scheint klobig.
Hier ist, womit ich anfange ... der Trick scheint zu täuschen 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"];
//....
}
iphone
ios
objective-c
core-data
Greg Combs
quelle
quelle
sectionNameKeyPath
wenn Sie Tausende von Datensätzen in der Datenbank haben.Antworten:
Der Ansatz von Dave DeLong ist zumindest in meinem Fall gut, solange Sie einige Dinge weglassen. So funktioniert es bei mir:
Fügen Sie der Entität "lastNameInitial" ein neues optionales Zeichenfolgenattribut hinzu (oder etwas in diesem Sinne).
Machen Sie diese Eigenschaft vorübergehend. Dies bedeutet, dass Core Data sich nicht die Mühe macht, es in Ihrer Datendatei zu speichern. Diese Eigenschaft ist nur dann im Speicher vorhanden, wenn Sie sie benötigen.
Generieren Sie die Klassendateien für diese Entität.
Machen Sie sich keine Sorgen um einen Setter für diese Eigenschaft. Erstellen Sie diesen Getter (das ist die halbe Magie, IMHO)
// 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"];
VORHERIGES: Das Befolgen von Daves ersten Schritten zu dem Brief erzeugte Probleme, bei denen er bei setPropertiesToFetch mit einer ungültigen Argumentausnahme stirbt. Ich habe den Code und die Debugging-Informationen unten protokolliert:
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"];
quelle
Ich glaube, ich habe noch eine weitere Option, diese verwendet eine Kategorie auf NSString ...
@implementation NSString (FetchedGroupByString) - (NSString *)stringGroupByFirstInitial { if (!self.length || self.length == 1) return self; return [self substringToIndex:1]; } @end
Jetzt etwas später beim Aufbau Ihres FRC:
- (NSFetchedResultsController *)newFRC { NSFetchedResultsController *frc = [[NSFetchedResultsController alloc] initWithFetchRequest:awesomeRequest managedObjectContext:coolManagedObjectContext sectionNameKeyPath:@"lastName.stringGroupByFirstInitial" cacheName:@"CoolCat"]; return frc; }
Dies ist jetzt mein Lieblingsansatz. Viel sauberer / einfacher zu implementieren. Darüber hinaus müssen Sie keine Änderungen an Ihrer Objektmodellklasse vornehmen, um dies zu unterstützen. Dies bedeutet, dass es für jedes Objektmodell funktioniert, sofern der Abschnittsname auf eine auf NSString basierende Eigenschaft verweist
quelle
The fetched object at index 14 has an out of order section name 'P. Objects must be sorted by section name'
Hier ist , wie Sie könnte es an die Arbeit:
Machen Sie sich keine Sorgen um einen Setter für diese Eigenschaft. Erstellen Sie diesen Getter (das ist die halbe Magie, IMHO)
Fordern Sie in Ihrer Abrufanfrage NUR diese PropertyDescription an (dies ist ein weiteres Viertel der Magie):
Stellen Sie sicher, dass Ihre Abrufanforderung NUR unterschiedliche Ergebnisse liefert (dies ist das letzte Viertel der Magie):
Bestellen Sie Ihre Ergebnisse mit diesem Brief:
Führen Sie die Anforderung aus und sehen Sie, was sie Ihnen gibt.
Wenn ich verstehe, wie dies funktioniert, wird vermutlich ein Array von NSManagedObjects zurückgegeben, von denen jedes nur die Eigenschaft lastNameInitial in den Speicher geladen hat und die eine Reihe unterschiedlicher Initialen für den Nachnamen sind.
Viel Glück und berichten Sie zurück, wie das funktioniert. Ich habe mir das nur aus dem Kopf gemacht und möchte wissen, ob das funktioniert. =)
Ich mag Greg Combs Antwort oben. Ich habe eine geringfügige Änderung vorgenommen, damit Zeichenfolgen wie "Smith" und "Smith" im selben Abschnitt angezeigt werden können, indem die Zeichenfolgen in Großbuchstaben umgewandelt werden:
- (NSString *)stringGroupByFirstInitial { NSString *temp = [self uppercaseString]; if (!temp.length || temp.length == 1) return self; return [temp substringToIndex:1]; }
quelle
Ich stoße die ganze Zeit auf dieses Problem. Die Lösung, auf die ich immer wieder zurückkomme, besteht darin, der Entität nur eine echte erste Anfangseigenschaft zu geben. Ein reales Feld zu sein, ermöglicht eine effizientere Suche und Reihenfolge, da Sie das Feld auf indiziert setzen können. Es scheint nicht zu viel Arbeit zu sein, die erste Initiale herauszuziehen und ein zweites Feld damit zu füllen, wenn die Daten zum ersten Mal importiert / erstellt werden. Sie müssen diesen anfänglichen Parsing-Code so oder so schreiben, aber Sie können ihn einmal pro Entität und nie wieder ausführen. Die Nachteile scheinen zu sein, dass Sie wirklich ein zusätzliches Zeichen pro Entität (und die Indizierung) speichern, das ist wahrscheinlich unbedeutend.
Eine zusätzliche Anmerkung. Ich scheue mich, den generierten Entitätscode zu ändern. Vielleicht fehlt mir etwas, aber die Tools zum Generieren von CoreData-Entitäten berücksichtigen keinen Code, den ich dort eingegeben habe. Jede Option, die ich beim Generieren des Codes auswähle, entfernt alle Anpassungen, die ich möglicherweise vorgenommen habe. Wenn ich meine Entitäten mit cleveren kleinen Funktionen fülle, muss ich dieser Entität eine Reihe von Eigenschaften hinzufügen, ich kann sie nicht einfach regenerieren.
quelle
schnell 3
Erstellen Sie zunächst eine Erweiterung für NSString (da CoreData grundsätzlich NSString verwendet).
extension NSString{ func firstChar() -> String{ if self.length == 0{ return "" } return self.substring(to: 1) } }
Sortieren Sie dann mit dem Schlüsselpfad firstChar, in meinem Fall lastname.firstChar
request.sortDescriptors = [ NSSortDescriptor(key: "lastname.firstChar", ascending: true), NSSortDescriptor(key: "lastname", ascending: true), NSSortDescriptor(key: "firstname", ascending: true) ]
Verwenden Sie zum Schluss den Schlüsselpfad firstChar für sectionNameKeyPath
let controller = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: "lastname.firstChar", cacheName: "your_cache_name")
quelle
Ich denke, ich habe einen besseren Weg, dies zu tun. Anstatt die vorübergehende Eigenschaft zu verwenden, wird die Ansicht angezeigt. Berechnen Sie die abgeleitete Eigenschaft des NSManagedObject neu und speichern Sie den Kontext. Nach den Änderungen können Sie die Tabellenansicht einfach neu laden.
Hier ist ein Beispiel für die Berechnung der Anzahl der Kanten jedes Scheitelpunkts und die Sortierung der Scheitelpunkte nach der Anzahl der Kanten. In diesem Beispiel ist Capsid der Scheitelpunkt, Touch die Kante.
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { [self.tableView endUpdates]; [self.tableView reloadData]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Capsid"]; NSError *error = nil; NSArray *results = [self.managedObjectContext executeFetchRequest:request error:&error]; if (error) { NSLog(@"refresh error"); abort(); } for (Capsid *capsid in results) { unsigned long long sum = 0; for (Touch *touch in capsid.vs) { sum += touch.count.unsignedLongLongValue; } for (Touch *touch in capsid.us) { sum += touch.count.unsignedLongLongValue; } capsid.sum = [NSNumber numberWithUnsignedLongLong:sum]; } if (![self.managedObjectContext save:&error]) { NSLog(@"save error"); abort(); } } - (NSFetchedResultsController *)fetchedResultsController { if (__fetchedResultsController != nil) { return __fetchedResultsController; } // Set up the fetched results controller. // Create the fetch request for the entity. NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; // Edit the entity name as appropriate. NSEntityDescription *entity = [NSEntityDescription entityForName:@"Capsid" inManagedObjectContext:self.managedObjectContext]; [fetchRequest setEntity:entity]; // Set the batch size to a suitable number. [fetchRequest setFetchBatchSize:20]; // Edit the sort key as appropriate. // NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"timeStamp" ascending:NO]; // NSSortDescriptor *sumSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"sum" ascending:NO]; // NSArray *sortDescriptors = [NSArray arrayWithObjects:sumSortDescriptor, nil]; [fetchRequest setReturnsDistinctResults:YES]; NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"sum" ascending:NO]; NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor]; [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:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil]; aFetchedResultsController.delegate = self; self.fetchedResultsController = aFetchedResultsController; NSError *error = nil; if (![self.fetchedResultsController performFetch:&error]) { /* Replace this implementation with code to handle the error appropriately. abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. */ NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } return __fetchedResultsController; }
quelle