NSFetchedresultsController sectionName KeyPath 基于当前位置和其他位置之间的范围
Posted
技术标签:
【中文标题】NSFetchedresultsController sectionName KeyPath 基于当前位置和其他位置之间的范围【英文标题】:NSFetchedresultController sectionNameKeyPath based on range between current location and other location 【发布时间】:2014-04-23 10:23:47 【问题描述】:我有一个entity
,里面有对象。这些对象有Latitude
和longitude
。我还添加了一个 transient attribute
range
来计算范围。请看下面的类:
@implementation Relation
@dynamic firstLetter;
@dynamic rel_address;
@dynamic rel_balanceTotal;
@dynamic rel_bank_country_code;
@dynamic rel_bank_number;
@dynamic rel_city;
@dynamic rel_city_id;
@dynamic rel_code;
@dynamic rel_country;
@dynamic rel_country_code;
@dynamic rel_customerProspect;
@dynamic rel_email;
@dynamic rel_expired_total;
@dynamic rel_fax;
@dynamic rel_gsm;
@dynamic rel_name;
@dynamic rel_phone;
@dynamic rel_turnovertotal;
@dynamic rel_vat_country_code;
@dynamic rel_vat_number;
@dynamic rel_website;
@dynamic rel_zipcode;
@dynamic rel_longitude;
@dynamic rel_latitude;
@dynamic range;
-(NSNumber *)range
NSNumber *rangeFromCurrentLocation;
[self willAccessValueForKey:@"range"];
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
CLLocation *location = [[CLLocation alloc]initWithLatitude:[self.rel_latitude floatValue] longitude:[self.rel_longitude floatValue]];
CLLocation *location2 = [[CLLocation alloc]initWithLatitude:appDelegate.latitude longitude:appDelegate.longitude];
CLLocationDistance distance = [location2 distanceFromLocation:location]; // distance is expressed in meters
CLLocationDistance kilometers = distance / 1000.0;
float floatrange = ceil(kilometers / 5.0) * 5;
rangeFromCurrentLocation = [NSNumber numberWithFloat:floatrange];
[self didAccessValueForKey:@"range"];
NSLog(@"RANGE IS %@",rangeFromCurrentLocation);
return rangeFromCurrentLocation;
我想要做的是建立一个包含 5 公里、10 公里、15 公里、...的路段的列表。 在启动时,我只加载半径为 5 公里的关系。我就是这样做的。
_searchDistance = 2.50;
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
float minLat = appDelegate.latitude - (_searchDistance / 69);
float maxLat = appDelegate.latitude + (_searchDistance / 69);
float minLon = appDelegate.longitude - _searchDistance / fabs(cos(appDelegate.longitude / 180.0 * M_PI)*69);
float maxLon = appDelegate.longitude + _searchDistance / fabs(cos(appDelegate.longitude / 180.0 * M_PI)*69);
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:@"Relation" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"rel_latitude <= %f AND rel_latitude >= %f AND rel_longitude <= %f AND rel_longitude >= %f", maxLat, minLat, maxLon, minLon];
[fetchRequest setPredicate:predicate];
NSSortDescriptor *sort = [[NSSortDescriptor alloc]
initWithKey:@"rel_name" ascending:YES selector:@selector(caseInsensitiveCompare:)];
[fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sort,nil]];
这工作正常,我只得到radius of 5km
中的对象。在我的 tableView 的底部,我有一个 load more button
,我在其中更改了 _searchDistance
(我又增加了 5 公里)
为了建立我的列表,我使用这个NSFetchedresultController
:
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:context sectionNameKeyPath:@"range"
cacheName:nil];
问题
当视图加载时,一切正常。但是当我点击加载更多时,我得到一个空列表并在我的日志中得到这个错误
CoreData: error: (NSFetchedResultsController) The fetched object at index 6 has an out of order section name '10. Objects must be sorted by section name'
2014-04-23 12:01:27.673 Adsolut[2097:60b] Unresolved error Error Domain=NSCocoaErrorDomain Code=134060 "The operation couldn’t be completed. (Cocoa error 134060.)" UserInfo=0x167630f0 reason=The fetched object at index 6 has an out of order section name '10. Objects must be sorted by section name',
reason = "The fetched object at index 6 has an out of order section name '10. Objects must be sorted by section name'";
任何帮助将不胜感激! 提前致谢!
【问题讨论】:
【参考方案1】:问题是所有对象都先按照第一个排序描述符排序,
然后根据sectionNameKeyPath
分组。
documentation 声明了这个参数:
如果这个key path和第一次排序指定的不一样 fetchRequest 中的描述符,它们必须生成相同的相对 订购。
这意味着您必须使用对所有对象进行排序的第一个排序描述符 根据他们到当前位置的距离。不幸的是,这种排序描述符 只能使用 persistent 属性。但是(据我所知)没有办法解决这个问题 如果您使用获取的结果控制器,则会受到限制。
【讨论】:
那你建议我怎么做? @StefGeelen:您可以计算距离并将其作为(永久)属性存储在实体中。当当前位置发生变化时,您必须重新计算它。 - 或者不使用 FRC 并实现您自己的代码将对象分组到部分中。 - 两种解决方案都不是 100% 令人满意,但这是 FRC 的限制。【参考方案2】:我同意@Martin R.
我使用的方法是在我的NSFetchedResultsController
中构建逻辑,这样,如果存在,第一个排序描述符始终是section
,其他排序描述符添加到排序描述符数组中作为第二个,第三个,第四,等等。
通过这种方式,您可以保护您的 FRC 免受当前遇到的崩溃。
更新...
为了进一步讨论,我已经包含了我经过稍微编辑的方法来准备 FRC,其中包括针对您遇到的崩溃的保护。如前所述,这可能不适用于瞬态属性。
此代码在触发方法之前设置了以下公共属性。
@property (nonatomic, strong) NSString *entity;
@property (nonatomic, strong) NSString *sectionIdentifier;
@property (nonatomic, strong) NSString *sortAttributePrimary;
@property (nonatomic, strong) NSString *sortAttributeSecondary;
@property (nonatomic, strong) NSString *sortAttributeTertiary;
@property (nonatomic, strong) NSString *sortAttributeQuaternary;
@property (nonatomic, strong) NSPredicate *requestPredicate;
@property (nonatomic, strong) NSString *cacheName;
这些属性中的每一个都由调用 FRC 的实体对象设置。当sectionIdentifier
存在值时,默认设置sortAttributePrimary = nil
。
还要注意(BOOL)userSettingListsSortAscending
是一个NSUserDefault
,它也在此方法的第一部分中确定,但已被删除以尝试将代码保持在合理的大小。
- (void)configureFetch
if (self.managedObjectContext)
...< other code including preparation of NSPredicate>...
// Prepare and set Sort Descriptors
NSArray *requestSortDescriptors = nil;
NSSortDescriptor *sortDescriptorPrimary = nil;
NSSortDescriptor *sortDescriptorSecondary = nil;
NSSortDescriptor *sortDescriptorTertiary = nil;
NSSortDescriptor *sortDescriptorQuaternary = nil;
if (self.sortAttributePrimary != nil)
sortDescriptorPrimary = [NSSortDescriptor sortDescriptorWithKey:self.sortAttributePrimary ascending:userSettingListsSortAscending selector:@selector(compare:)];
else if (self.sortAttributePrimary == nil)
sortDescriptorPrimary = [NSSortDescriptor sortDescriptorWithKey:self.sectionIdentifier ascending:userSettingListsSortAscending];
if (self.sortAttributeSecondary != nil)
sortDescriptorSecondary = [NSSortDescriptor sortDescriptorWithKey:self.sortAttributeSecondary ascending:userSettingListsSortAscending selector:@selector(compare:)];
if (self.sortAttributeTertiary != nil)
sortDescriptorTertiary = [NSSortDescriptor sortDescriptorWithKey:self.sortAttributeTertiary ascending:userSettingListsSortAscending selector:@selector(compare:)];
if (self.sortAttributeQuaternary != nil)
sortDescriptorQuaternary = [NSSortDescriptor sortDescriptorWithKey:self.sortAttributeQuaternary ascending:userSettingListsSortAscending selector:@selector(compare:)];
if (sortDescriptorPrimary != nil)
if (sortDescriptorSecondary != nil)
if (sortDescriptorTertiary != nil)
if (sortDescriptorQuaternary != nil)
requestSortDescriptors = [NSArray arrayWithObjects: sortDescriptorPrimary, sortDescriptorSecondary, sortDescriptorTertiary, sortDescriptorQuaternary, nil];
else if (sortDescriptorQuaternary == nil)
requestSortDescriptors = [NSArray arrayWithObjects: sortDescriptorPrimary, sortDescriptorSecondary, sortDescriptorTertiary, nil];
else if (sortDescriptorTertiary == nil)
requestSortDescriptors = [NSArray arrayWithObjects: sortDescriptorPrimary, sortDescriptorSecondary, nil];
else if (sortDescriptorSecondary == nil)
requestSortDescriptors = [NSArray arrayWithObjects: sortDescriptorPrimary, nil];
NSLog(@"%@ - %@ - sortDescriptors are: %@_", self.className, NSStringFromSelector(_cmd), requestSortDescriptors);
[fetchRequest setSortDescriptors:requestSortDescriptors];
// Set fetchedResultsController
if (self.sectionIdentifier == nil || [self.sectionIdentifier isEqualToString:@""])
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:self.cacheName];
else
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:self.sectionIdentifier cacheName:self.cacheName];
self.fetchedResultsController.delegate = self;
else
self.fetchedResultsController = nil;
最后值得注意的是,我使用compare:
而不是localizedCaseInsentiveCompare:
进行排序,因为有时我的排序描述符之一是NSDate
。
【讨论】:
有很多示例,其中节键与第一个排序描述符不同。在这个问题中,第一个排序描述符可能是到当前位置的距离,而 section key 可能是四舍五入到 5(km)的倍数的距离。 - 另一个例子是当对象按日期排序时,部分键只是年-月-日。 @MartinR 你建议我能做什么? 是否值得发布我的代码来说明我如何做到这一点?我承认我没有使用瞬态属性作为我的部分,但它可能会为解决方案提供一些线索。 @andrewbuilder 任何帮助表示赞赏!以上是关于NSFetchedresultsController sectionName KeyPath 基于当前位置和其他位置之间的范围的主要内容,如果未能解决你的问题,请参考以下文章