按与当前位置的距离对 TableView 进行排序

Posted

技术标签:

【中文标题】按与当前位置的距离对 TableView 进行排序【英文标题】:Sort TableView by distance from current location 【发布时间】:2012-06-20 01:42:52 【问题描述】:

我有一个包含经纬度信息的 SQL 数据库。我找到了我当前的位置,并且能够获得每个位置与我当前位置的距离。我可以让我的 tableview 显示这个距离,但我知道我想对该 tableview 进行排序,以便首先列出最接近的。

我的想法是我需要将距离添加到我的 SQL DB 数据中,以某种方式对其进行排序,然后将该信息显示回 tableview。

我的想法是我会在我的 TableView 中完成所有这些工作。寻找有关如何执行此操作以及是否应该在 tableview 中执行此操作的指导。

#import "TulsaMasterViewController.h"
#import "TulsaDetailViewController.h"
#import "Bars.h"
#import "BarDatabase.h"

@implementation TulsaMasterViewController

@synthesize barArray = _barArray;
@synthesize currentLat = _currentLat;


- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation

currentLat = newLocation;

if (newLocation.horizontalAccuracy <= 100.0f) 
    [lm stopUpdatingLocation];


[self.tableView reloadData];


- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error

NSString *msg = [[NSString alloc]initWithString:@"Error obtaining location"];
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"Error" message:msg delegate:nil cancelButtonTitle:@"Done" otherButtonTitles:nil];
[alert show];


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
return [self.barArray count];


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 

static NSString *CellIdentifier = @"Cell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) 
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];


//Get the object from the array.
Bars *barObj = [self.barInfo objectAtIndex:indexPath.row];

//Set the name.
cell.textLabel.text = barObj.barName;

if (currentLat == nil) 
    cell.detailTextLabel.text = [NSString stringWithFormat:@"?"];
else

    cell.detailTextLabel.text = [NSString stringWithFormat:@"%.02f", cachedDist];


// Set up the cell
return cell;


- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender 
if ([[segue identifier] isEqualToString:@"ShowDetails"]) 
    TulsaDetailViewController *detailViewController = [segue destinationViewController];

    detailViewController.detailItem = [self.barArray objectAtIndex:[self.tableView indexPathForSelectedRow].row];



- (void)viewDidLoad

[super viewDidLoad];

self.barArray = [BarDatabase database].barInfo;

lm = [[CLLocationManager alloc] init];
lm.delegate = self;
[lm startUpdatingLocation];


- (void)viewWillAppear:(BOOL)animated

[super viewWillAppear:animated];

for (Bars *barObj in barArray) 
    NSString *strLat = barObj.Lat;
    NSString *strLong = barObj.Long;
    CLLocation *barLocation = [[CLLocation alloc] initWithLatitude:[strLat doubleValue] longitude:[strLong doubleValue]];
    CLLocationDistance distance = [currentLat distanceFromLocation:barLocation]/1000;

    [barArray addObject:[NSNumber numberWithDouble:distance]];
    NSSortDescriptor *sort=[NSSortDescriptor sortDescriptorWithKey:@"cachedDist" ascending:YES];
    [barArray sortUsingDescriptors:[NSArray arrayWithObject:sort]];

我的其余代码供参考

酒吧.h

#import <Foundation/Foundation.h>

@interface Bars : NSObject 
    NSString *barName;
    NSString *barAddress;
    NSString *Lat;
    NSString *Long;
    NSString *cachedDist;


@property (nonatomic, copy) NSString *barName;
@property (nonatomic, copy) NSString *barAddress;
@property (nonatomic, copy) NSString *Lat;
@property (nonatomic, copy) NSString *Long;
@property (nonatomic, copy) NSString *cachedDist;

- (id)initWithName:(NSString *)name address:(NSString *)address latitude:(NSString *)latitude longitude:(NSString *)longitude distance:(NSString *)distance;

@end

酒吧.m

#import "Bars.h"

@implementation Bars

@synthesize barName = _barName;
@synthesize barAddress = _barAddress;
@synthesize Lat = _Lat;
@synthesize Long = _Long;
@synthesize cachedDist = _cachedDist;

- (id)initWithName:(NSString *)name address:(NSString *)address latitude:(NSString *)latitude longitude:(NSString *)longitude distance:(NSString *)distance;

if ((self = [super init])) 
    self.barName = name;
    self.barAddress = address;
    self.Lat = latitude;
    self.Long = longitude;
    self.cachedDist = distance;

return self;


@end

BarDatabase.h

#import <Foundation/Foundation.h>
#import <sqlite3.h>

@interface BarDatabase : NSObject

    sqlite3 *_database;


+ (BarDatabase *)database;
- (NSMutableArray *)barInfo;

@end

BarDatabase.m

#import "BarDatabase.h"
#import "Bars.h"

@implementation BarDatabase

static BarDatabase *_database;

+ (BarDatabase *)database 
if (_database == nil) 
    _database = [[BarDatabase alloc] init];

return _database;


- (id)init 
if ((self = [super init])) 
    NSString *sqLiteDb = [[NSBundle mainBundle] pathForResource:@"TulsaBars" 
                                                         ofType:@"sqlite"];

    if (sqlite3_open([sqLiteDb UTF8String], &_database) != SQLITE_OK) 
        NSLog(@"Failed to open database!");
    

return self;


- (void)dealloc 
sqlite3_close(_database);


- (NSMutableArray *)barInfo 

NSMutableArray *retval = [[NSMutableArray alloc] init];
NSString *query = @"SELECT * FROM TulsaBars";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(_database, [query UTF8String], -1, &statement, nil) 
    == SQLITE_OK) 
    while (sqlite3_step(statement) == SQLITE_ROW) 
        char *nameChars = (char *) sqlite3_column_text(statement, 1);
        char *addressChars = (char *) sqlite3_column_text(statement, 2);
        char *latChars = (char *) sqlite3_column_text(statement, 8);
        char *longChars = (char *) sqlite3_column_text(statement, 9);
        NSString *name = [[NSString alloc] initWithUTF8String:nameChars];
        NSString *address = [[NSString alloc] initWithUTF8String:addressChars];
        NSString *latitude = [[NSString alloc] initWithUTF8String:latChars];
        NSString *longitude = [[NSString alloc] initWithUTF8String:longChars];

        Bars *info = [[Bars alloc]
                      initWithName:name address:address latitude:latitude longitude:longitude];                        
        [retval addObject:info];
    
    sqlite3_finalize(statement);

return retval;


@end

【问题讨论】:

【参考方案1】:

嗯。在 cellForRowAtIndexPath 内不应该发生排序。在游戏中为时已晚。如果每个单元格为 1 个条形,并且条形应按距离排序,我建议在您的 self.barInfo 的惰性 getter 中实现排序(如果这是您的条形列表)。

我不确定您的模型是什么样的,但如果每个条形对象都有一个与您的参考点的距离属性,那么您的排序看起来与您在那里的相似。

这种排序只会在第一个单元格被加载(即“惰性”部分)然后被缓存时发生。所以你的 cellForRowAtIndexPath: 只会在它的 indexPath 行中请求模型对象,相信它的顺序是正确的。

我不确定您对这个术语了解多少,所以请随时提出澄清问题,我会重复回答。

分享更多代码后编辑: 我认为您应该向名为 cachedDistance 之类的栏添加一个@property,并且每当您进入屏幕(viewWillAppear)时,迭代这些栏并设置它们的缓存距离(使用与 cellForRow 中类似的代码...)。然后为 self.barArray 实现一个 getter:-(NSArray *)barArray,它本质上返回使用 sortDescriptor 排序的 barArray,其中缓存的距离属性的名称作为它的键。这将大大简化您的 cellForRow... 代码。

然后,您可以扩展逻辑以在获取位置更新时重新计算距离,也许只有当它与之前的距离有一定距离时。

【讨论】:

属性(非原子,复制) NSString *barName;属性(非原子,复制) NSString *barAddress;属性(非原子,复制) NSString *Lat;属性(非原子,复制) NSString *Long;最初,我的数据库中没有实际的距离场。在 cellForRowAtIndexPath 下,我正在计算加载每一行时的距离。我认为为了保持与正确行相关的距离,我需要将距离添加到数组并在 cellForRowAtIndexPath 下排序。设置 Bars.h/m 定义字段 BarDatabase.h/m 拉取数据 位置在 MasterViewController 可以发你的代码 您可以使用它来编辑您的初始帖子,这将允许格式化并且其他人会注意到它:-) 我想我明白了。让我消化一下再回复你。 我已经更新了上面的代码以开始执行您上面所说的操作,但是我在 viewWillAppear 下遇到了第一个问题。它不喜欢 indexPath。我倾向于同意,但我不确定如何拉每一行来计算我的距离。如果我误解了你,请告诉我。 viewWillAppear 中的逻辑需要更新,不能和cellForRow 中的逻辑一样。应该对您的条进行迭代,设置它们的每个缓存距离。只需仔细考虑整个过程。在 viewWillAppear 中,您只需要“设置”数组,这涉及加载条形图,然后迭代它们中的每一个(而不是尝试使用索引路径加载一个)并计算/设置距离。然后对数组进行排序并将其存储以供以后在 cellForRow 中使用...

以上是关于按与当前位置的距离对 TableView 进行排序的主要内容,如果未能解决你的问题,请参考以下文章

在 Meteor 中按与当前用户的距离对用户进行反应性排序?

html5,移动:按距离对 POI 进行排序

根据额外参数对向量进行排序

如何根据Android中当前位置的距离对地理点进行排序

Swift如何按距离对表格视图进行排序

使用空间分析函数和数据类型在 MySQL 中按距离排序