按距离排序UITableView

我试图按照从坐标计算的距离按升序对我的tableview进行排序。 一切都像魅力一样,除了我不能按升序排列,我一直在用NSSortDescriptor等等,但是运气NSSortDescriptor ,任何帮助都会受到赞赏,这是我的代码:

 - (void) retrieveData { NSURL *url = [NSURL URLWithString:jsonFile]; NSData *data = [NSData dataWithContentsOfURL:url]; _jsonArray = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]; _salesArray = [[NSMutableArray alloc]init]; for (int i = 0; i  0) { CLPlacemark *placemark = [placemarks objectAtIndex:0]; CLLocation *location = placemark.location; CLLocation *myLocation = self.manager.location; CLLocationDistance miles = [location distanceFromLocation:myLocation]; //this is the variable i want in my convenience init. distance = [NSString stringWithFormat:@"%.1f m", (miles/1609.344)]; } }]; [_salesArray addObject:[[sales alloc] initWithSales:sID andName:sName andAddress:sAddress andPostcode:distance]]; } [_salesArray sortUsingComparator: ^NSComparisonResult(id obj1, id obj2){ sales *p1 = (sales *)obj1; sales *p2 = (sales *)obj2; if (p1.postcode > p2.postcode) { return (NSComparisonResult)NSOrderedDescending; } if (p1.postcode < p2.postcode) { return (NSComparisonResult)NSOrderedAscending; } return (NSComparisonResult)NSOrderedSame; } ]; [self.tableView reloadData]; } 

这里有一些问题:

  1. geocodeAddressString强加了一些限制,如文档中所述:

    此方法异步将指定的位置数据提交给地理编码服务器并返回。 您的完成处理程序块将在主线程上执行。 在发起前向地理编码请求后,请勿尝试启动另一个前向或反向地理编码请求。

    对于每个应用程序,地理编码请求都是速率限制的,因此在短时间内提出过多请求可能会导致某些请求失败。 超过最大速率时,地理编码器会将值为kCLErrorNetwork的错误对象传递给完成处理程序。

    这里有几个重要观察:

    • 这是异步运行的(因此您无法调用geocodeAddressString并在之后立即使用其结果)。 您已根据完成块的地理编码调用工作。

    • 在前一个地理编码请求完成之前,您不应该开始下一个地理编码请求。

    这意味着您必须对第一个邮政编码进行地理编码,让它以异步方式完成(即稍后),对下一个邮政编码进行地理编码,让它完成等等,然后进行排序并重新加载表格。 一个简单的for循环不是一个合适的方法。 您可以编写一个执行单个地理编码的方法并调用完成块中的下一个地理编码,也可以使用NSOperation子类,如下所示。

  2. 我建议将distance存储为NSNumber 。 在MVC中,一个小数位字符串表示是“视图”行为,并且可能不应该是“模型”的一部分。

    这样做的好处是,当您想要对对象进行排序时,您只需调用NSNumbercompare方法即可。 例如,如果salesPersonnel是一个NSMutableArray对象,每个SalesPerson对象都有一个名为distanceNSNumber属性,那么您可以执行以下操作:

     [self.salesPersonnel sortUsingComparator:^NSComparisonResult(SalesPerson *obj1, SalesPerson *obj2) { return [obj1.distance compare:obj2.distance]; }]; 

    我不确定你的每个实际销售交易或销售人员的sales条目,所以如果我误解了对象类型,我很抱歉,但希望这说明了这个想法。


你可以按照你想要的任何方式做到这一点,但对我来说,当我想运行一些异步任务,但按顺序执行时,我会倾向于并发NSOperation子类,我将其添加到一个串行的NSOperationQueue

 NSError *error; NSArray *addressEntries = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; NSAssert(addressEntries, @"unable to parse: %@", error); NSOperationQueue *queue = [[NSOperationQueue alloc] init]; queue.maxConcurrentOperationCount = 1; self.salesPersonnel = [NSMutableArray array]; // define sort operation that will be called when all of the geocode attempts are done NSOperation *sortAndReloadTableOperation = [NSBlockOperation blockOperationWithBlock:^{ [self.salesPersonnel sortUsingComparator:^NSComparisonResult(SalesPerson *obj1, SalesPerson *obj2) { return [obj1.distance compare:obj2.distance]; }]; [self.tableView reloadData]; }]; // create the geocode operations for (NSDictionary *addressEntry in addressEntries) { SalesPerson *salesPerson = [[SalesPerson alloc] initWithSalesId:addressEntry[@"id"] name:addressEntry[@"name"] address:addressEntry[@"address"] postalCode:addressEntry[@"postcode"]]; [self.salesPersonnel addObject:salesPerson]; NSOperation *geocodeOperation = [[GeocodeOperation alloc] initWithPostalCode:salesPerson.postalCode completionHandler:^(NSArray *placemarks, NSError *error) { CLPlacemark *placemark = [placemarks firstObject]; CLLocation *location = placemark.location; CLLocationDistance meters = [location distanceFromLocation:self.currentLocation]; salesPerson.distance = @(meters / 1609.344); }]; [sortAndReloadTableOperation addDependency:geocodeOperation]; // note, the final sort is dependent upon this finishing [queue addOperation:geocodeOperation]; // go ahead and queue up the operation } // now we can queue the sort and reload operation, which won't start until the geocode operations are done [[NSOperationQueue mainQueue] addOperation:sortAndReloadTableOperation]; 

GeocodeOperation是一个基本的并发NSOperation子类:

 // GeocodeOperation.h #import  typedef void(^GeocodeCompletionHandler)(NSArray *placemarks, NSError *error); @interface GeocodeOperation : NSOperation @property (nonatomic, copy) GeocodeCompletionHandler geocodeCompletionHandler; - (instancetype)initWithPostalCode:(NSString *)postalCode completionHandler:(GeocodeCompletionHandler)geocodeCompletionHandler; @end 

和实现(注意, main方法是这里唯一有趣的一点……所有其余的是常规并发NSOperation子类代码;个人而言,我将所有并发的NSOperation内容移动到一个基类中,它清理了这个GeocodeOperation代码,但我不想再混淆这个,所以我保持这个简单):

 // GeocodeOperation.m #import "GeocodeOperation.h" @import CoreLocation; @interface GeocodeOperation () @property (nonatomic, readwrite, getter = isFinished) BOOL finished; @property (nonatomic, readwrite, getter = isExecuting) BOOL executing; @property (nonatomic, copy) NSString *postalCode; @end @implementation GeocodeOperation @synthesize finished = _finished; @synthesize executing = _executing; - (CLGeocoder *)sharedGeocoder { static CLGeocoder *geocoder = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ geocoder = [[CLGeocoder alloc]init]; }); return geocoder; } - (instancetype)initWithPostalCode:(NSString *)postalCode completionHandler:(GeocodeCompletionHandler)geocodeCompletionHandler { self = [super init]; if (self) { _postalCode = [postalCode copy]; _geocodeCompletionHandler = geocodeCompletionHandler; } return self; } - (void)main { [[self sharedGeocoder] geocodeAddressString:self.postalCode completionHandler:^(NSArray *placemarks, NSError *error) { if (self.geocodeCompletionHandler) { self.geocodeCompletionHandler(placemarks, error); } [self completeOperation]; }]; } #pragma mark - NSOperation methods - (void)start { if ([self isCancelled]) { self.finished = YES; return; } self.executing = YES; [self main]; } - (void)completeOperation { self.executing = NO; self.finished = YES; } - (BOOL)isConcurrent { return YES; } - (void)setExecuting:(BOOL)executing { if (_executing != executing) { [self willChangeValueForKey:@"isExecuting"]; _executing = executing; [self didChangeValueForKey:@"isExecuting"]; } } - (void)setFinished:(BOOL)finished { if (_finished != finished) { [self willChangeValueForKey:@"isFinished"]; _finished = finished; [self didChangeValueForKey:@"isFinished"]; } } @end 

我认为问题是邮政编码是一个NSString 。 所以在你的块(p1.postcode > p2.postcode)中比较ADDRESS LOCATIONS,而不是字符串值本身。

你想使用NSString函数compare:而不是自己做。

尝试这个:

 [_salesArray sortUsingComparator: ^NSComparisonResult(id obj1, id obj2){ sales *p1 = (sales *)obj1; sales *p2 = (sales *)obj2; NSString *postcode1 = p1.postcode; NSString *postcode2 = p2.postcode; return [postcode1 compare:posecode2]; ];