NSFetchedresultsController sectionNameKeyPath基于当前位置和其他位置之间的范围

时间:2014-04-23 10:23:48

标签: ios iphone objective-c core-data nsfetchedresultscontroller

我有entity个对象。这些对象包含Latitudelongitude。我还添加了一个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公里的路段的列表.... 在启动时,我只加载半径为5km的关系。我是这样做的。

_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'";

任何帮助将不胜感激! 提前谢谢!

2 个答案:

答案 0 :(得分:1)

问题是所有对象首先按照第一个排序描述符进行排序, 然后根据sectionNameKeyPath分组成各个部分。 有关此参数的documentation个说明:

  

如果此键路径与第一个排序指定的路径不同   在fetchRequest中的描述符,它们必须生成相同的相对   排序

这意味着您必须使用排序所有对象的第一个排序描述符 根据他们到当前位置的距离。不幸的是,这种排序描述符 只能使用持久性属性。但是(据我所知)没有办法解决这个问题 如果使用获取的结果控制器,则限制。

答案 1 :(得分:0)

我同意@Martin R。

我使用的方法是在我的NSFetchedResultsController中构建逻辑,这样,如果它存在,第一个排序描述符总是section,其他排序描述符被添加到排序描述符数组中第二,第三,第四等

通过这种方式,您可以保护FRC免受当前遇到的崩溃的影响。

<强> UPDATE ...

除了讨论之外,我还包括了一些我编辑过的方法来准备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的实体对象设置。默认情况下,sortAttributePrimary = nil存在值时设置sectionIdentifier

另请注意,(BOOL)userSettingListsSortAscendingNSUserDefault,也是在此方法的第一部分中确定的,但是已被删除以尝试将代码保持在合理的大小。

- (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