按距离排序UITableView

时间:2014-07-14 23:50:09

标签: ios uitableview sorting nssortdescriptor

我正在尝试按照从坐标计算的距离按升序对我的tableview进行排序。一切都像魅力一样,除了我不能按升序排序,我一直在用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 < _jsonArray.count; i++) {

        NSString *sID = [[_jsonArray objectAtIndex:i] objectForKey:@"id"];
        NSString *sName = [[_jsonArray objectAtIndex:i] objectForKey:@"name"];
        NSString *sAddress = [[_jsonArray objectAtIndex:i] objectForKey:@"address"];
        NSString *sPostcode = [[_jsonArray objectAtIndex:i] objectForKey:@"postcode"];


        __block NSString *distance;
        CLGeocoder *geocoder = [[CLGeocoder alloc]init];
        [geocoder geocodeAddressString:sPostcode completionHandler:^(NSArray *placemarks,        NSError *error) {


            if (error == nil && placemarks.count > 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];
}

2 个答案:

答案 0 :(得分:2)

这里有一些问题:

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

      

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

         

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

    这里有几个重要的观察结果:

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

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

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

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

    这样做的好处是,当您想要对对象进行排序时,您只需调用compare的{​​{1}}方法即可。例如,如果NSNumbersalesPersonnel个对象,其中每个NSMutableArray对象都具有SalesPerson属性NSNumber,那么您可以执行以下操作:

    distance

    我不确定您的每个实际销售交易或销售人员是否有[self.salesPersonnel sortUsingComparator:^NSComparisonResult(SalesPerson *obj1, SalesPerson *obj2) { return [obj1.distance compare:obj2.distance]; }]; 个条目,所以如果我误解了对象类型,我会道歉,但希望这说明了这个想法。


  3. 你可以按照你想要的任何方式做到这一点,但对我来说,当我想要运行许多异步任务,但按顺序执行时,我会倾向于并发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 <Foundation/Foundation.h> typedef void(^GeocodeCompletionHandler)(NSArray *placemarks, NSError *error); @interface GeocodeOperation : NSOperation @property (nonatomic, copy) GeocodeCompletionHandler geocodeCompletionHandler; - (instancetype)initWithPostalCode:(NSString *)postalCode completionHandler:(GeocodeCompletionHandler)geocodeCompletionHandler; @end 方法在这里是唯一有趣的位...所有其余的是例程并发main子类代码;就个人而言,我移动所有并发{{1填充到基类中,它会清除这个NSOperation代码,但我不想再混淆这个,所以我保持这个简单):

    NSOperation

答案 1 :(得分:0)

我认为问题是邮政编码是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];
 ];