核心数据中的位置按NSFetchedResultsController的距离排序?

时间:2012-12-06 19:31:44

标签: ios core-data geolocation nssortdescriptor nsfetchedresultscontroller

我的iOS Core Data数据库中有一组实体对象,用于描述某个位置的内容。我们称之为实体位置。我通过在Location上有两个属性来实现这一点,这两个属性引用了位置 - 纬度和经度,两个都是双倍的。还有其他元素,如名称。

我正在使用NSFetchedResultsController将实体绑定到UITableViewController。我想要做的是将结果按距离给定的CLLocationCoordinate2D排序。在一个非常理想的场景中,我能够刷新该列表以基于新位置重新计算排序。因此,这种类型取决于两个键,以及第三个“静态”变量(一个不会在集合中的项目之间变化的变量)。

如果我使用NSSortDescriptors对任意列表进行排序,我想我可以弄清楚如何做到这一点。但是,我不控制在NSFetchedResultsController中如何使用排序描述符。

有没有办法可以配置我的实体,我的NSFetchedResultsController,我的NSSortDescriptors等来完成这个?我怀疑答案不在于创建一个花哨的NSSortDescriptor,而是在实体中创建一个瞬态属性来代表与我的距离,并定期重新计算该属性。但是,我对核心数据足够新,我不确定如何做到这一点(迭代所有实体并重新计算字段)。我也不确定NSSortDescriptors是否可以处理Transient属性。

2 个答案:

答案 0 :(得分:13)

(来自评论:)

(基于SQLite的)Core Data存储的获取请求不能使用基于瞬态属性或基于Objective-C的谓词的排序描述符。

如果您不想放弃获取结果控制器的优点(如动画表视图更新,自动分组到部分等),您必须预先计算到当前位置的距离并将其存储在(持久性的对象属性。

或者,您可以获取所有对象并在内存中对它们进行排序。在这种情况下,您可以使用任意排序描述符。但是这不能与获取的结果控制器结合使用,因此您必须在托管对象上下文中注册更改并在必要时重新加载表。

答案 1 :(得分:0)

我发现BSFetchedResultsController github project是一个NSFetchResultsController子类,它执行Martin建议的内容,它使用任意排序描述符在内存中进行排序,此外它还注册了上下文的更改并再次计算任何索引更改帐户任意排序描述符。总而言之,这是一项令人印象深刻我成功地用它按距离排序如下:

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]);
}

由于它是一个旧项目,它没有ARC,所以当你包含这两个文件时,记得在Target,Build Phases中使用Compiler Flags -fno-objc-arc标记.m。另请注意,开发人员认为代码不是生产就绪的,所以如果使用它,请确保进行充分的测试。

在上面的代码中,我在Venue托管对象子类上有一个瞬态属性coreLocation,你可以看到如何实现here。此外,距离计算效率低下,您可能希望缓存对象中的距离,而不是在每次比较时重新计算距离。

最后,看来这个项目是因为创造者丹尼尔索普的Stackoverflow问题没有得到解决,导致他解决问题并自己发布唯一的答案,所以我想如果你发现他的项目很有用你就可以{{{ 3}}就像我一样。