7

Ich habe eine Reihe von Entitätsobjekten in meiner iOS Core Data-Datenbank, die etwas an einem Ort beschreiben. Lassen Sie uns die Entity Location nennen. Ich habe dies implementiert, indem ich zwei Attribute auf Location habe, die sich auf den Standort beziehen - Breite und Länge, beide Doubles. Es gibt andere Elemente, wie den Namen.Standorte in Core-Daten nach Entfernung über NSFetchedResultsController sortiert?

Ich verwende einen NSFetchedResultsController, um die Entitäten an einen UITableViewController zu binden. Ich möchte die Ergebnisse nach Entfernung zu einer bestimmten CLLocationCoordinate2D sortiert haben. In einem wirklich idealen Szenario kann ich diese Liste aktualisieren, um die Sortierung basierend auf einem neuen Speicherort neu zu berechnen. Daher hängt diese Art von zwei Schlüsseln und einer dritten "statischen" Variablen ab (eine, die nicht über die Elemente in der Sammlung variiert).

Ich denke ich könnte herausfinden, wie das geht, wenn ich eine willkürliche Liste mit NSSortDescriptors sortiere. Ich kontrolliere jedoch nicht, wie die Sortierdeskriptoren in einem NSFetchedResultsController verwendet werden.

Gibt es eine Möglichkeit, dass ich meine Entitäten, meine NSFetchedResultsController, meine NSSortDescriptors usw. konfigurieren könnte, um dies zu erreichen? Ich vermute, dass die Antwort nicht darin besteht, einen ausgefallenen NSSortDescriptor zu erstellen, sondern stattdessen ein transientes Attribut in der Entität zu erstellen, die die Distanz zu mir darstellt, und dieses Attribut periodisch neu zu berechnen. Ich bin jedoch neu genug für Core Data, dass ich nicht sicher bin, wie ich das am besten mache (über alle Entitäten iterieren und ein Feld neu berechnen). Ich bin mir auch nicht sicher, ob NSSortDescriptors mit transienten Attributen arbeiten wird.

+0

Leider kann ein abgerufener Ergebnis-Controller nicht nach transienten Attributen oder berechneten Eigenschaften sortieren. Siehe http://stackoverflow.com/questions/13292582/nspredicate-with-function-not-working oder http://stackoverflow.com/questions/12027769/nssortdescriptor-sort-by-location für ähnliche Probleme und Verweise auf die Dokumentation. –

+0

Vielen Dank! Ich habe das gesehen, und deshalb dachte ich, dass die Antwort darin bestehen könnte, Werte in meinen Kerndaten-Entitäten zu berechnen. Lassen Sie uns Transient vom Tisch nehmen - wenn es kein transientes Feld ist, wie würde ich dann vorgehen, um den Satz von Entitäten regelmäßig zu aktualisieren? –

+0

Benötigen Sie wirklich einen abgerufenen Ergebnis-Controller? Wenn Sie die Objekte in ein Array abrufen, können Sie das Array im Speicher sortieren und als Datenquelle für die Tabellenansicht verwenden. –

Antwort

12

(Aus den Kommentaren :)

A Abrufanforderung für einen (SQLite basiert) Speicher Core Data kann nicht sortieren Deskriptoren auf transiente Attribute oder Objective-C basierten Prädikaten verwenden basiert.

Wenn Sie nicht die Vorteile eines abgerufenen Ergebniscontrollers (wie animierte Tabellenansichtsupdates, automatische Gruppierung in Abschnitte usw.) verlieren möchten, müssen Sie den Abstand zum aktuellen Speicherort vorberechnen und in a speichern (persistent) Attribut Ihrer Objekte.

Alternativ können Sie alle Objekte abrufen und im Speicher sortieren. In diesem Fall können Sie beliebige Sortierdeskriptoren verwenden. Dies kann jedoch nicht mit einem abgerufenen Ergebnis-Controller kombiniert werden, so dass Sie sich für Änderungen im Kontext des verwalteten Objekts registrieren und die Tabelle bei Bedarf neu laden müssen.

0

Ich entdeckte die BSFetchedResultsController github project, die eine NSFetchResultsController-Unterklasse ist, die tut, was Martin vorgeschlagen, dass es im Speicher mit einem beliebigen Sortierdeskriptor sortiert, außerdem registriert es auch Änderungen am Kontext und berechnet etwaige Indexänderungen wieder unter Berücksichtigung der willkürlichen Sortierdeskriptor. Alles in allem eine sehr beeindruckende Leistung! Ich benutzte es erfolgreich nach Entfernung sortiert werden, wie folgt:

BSFetchedResultsController* fetchedResultsController = [[BSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:nil]; 

// create a location to compare distance to, e.g. current location 
CLLocation* sourceLocation = [[CLLocation alloc] initWithLatitude:55.87595153937809 longitude:-4.2578177698913855]; 

// compare the distance from both to the source location 
fetchedResultsController.postFetchComparator= ^(id a, id b) { 
    Venue* v1 = (Venue*)a; 
    Venue* v2 = (Venue*)b; 

    double d1 = [v1.coreLocation distanceFromLocation:sourceLocation]; 
    double d2 = [v2.coreLocation distanceFromLocation:sourceLocation]; 

    return [@(d1) compare:@(d2)]; 
}; 

NSError *error = nil; 
if (![fetchedResultsController performFetch:&error]) { 
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 
} 

Seit seiner alten Projekt ist es nicht ARC haben, so dass, wenn Sie sind die beiden Dateien erinnere mich an die .m mit Compiler-Flags markieren -fno-objc -arc im Ziel, Build-Phasen. Beachten Sie außerdem, dass der Entwickler der Meinung ist, dass der Code nicht produktionsbereit ist. Stellen Sie daher sicher, dass Sie bei der Verwendung ausreichende Tests durchführen.

In meinem Code oben habe ich eine vorübergehende Eigenschaft coreLocation auf meinem Venue verwalteten Objekt Unterklasse, können Sie sehen, wie das here zu erreichen. Auch die Entfernungsberechnung ist ineffizient. Sie können die Entfernung im Objekt zwischenspeichern, anstatt sie bei jedem Vergleich neu zu berechnen.

Schließlich scheint dieses Projekt zu sein, weil der Schöpfer Daniel Thorpe's Stackoverflow Frage unbeantwortet bleibt, was ihn dazu bringt, das Problem zu lösen und die einzige Antwort selbst zu posten, also ich denke, wenn Sie sein Projekt nützlich finden, könnten Sie bitte up-vote his post wie ich es tat.

Verwandte Themen