向 MKMapView 添加注释的最佳方法
Posted
技术标签:
【中文标题】向 MKMapView 添加注释的最佳方法【英文标题】:Optimal way to add annotations to MKMapView 【发布时间】:2012-02-28 06:06:55 【问题描述】:我从数据库中提取地址列表。列表的大小可以是 0-n。但其中 n 通常为 50-60。
我正在尝试发现将所有这些位置添加到地图视图的最佳方法。
我目前使用的方法如下。
我创建了一个 mutableArray(注释)来保存我的所有注释(我经常删除并添加它们,因此最好将它们全部放在手边以便快速添加/删除)。
我运行了我加载的地址列表(来自 DB),将地址传递给反向地理编码器以获得纬度和经度。
检查 lat 和 long 是否为 0,0(意味着我受到 google 的速率限制)。
Y - 如果是这样,我标记已禁止速率并将 @"" 添加到注释数组。在添加和删除时,必须保留注释以便正确参考。
N - 如果没有,那么我将该注释添加到地图和注释数组中。
如果有速率禁令,我会遍历注解数组寻找@"",找到后,继续执行步骤 2-5。
我递归调用此函数(第 5 步),直到添加所有注释。但这似乎效率低下。
有没有人发现一种更节省时间和内存的方法?
这是注释加载的初始调用:
- (void)addLocations
/*
Setup an array to hold all annotations, this is done for fast removal/addition when filtering which happens often. So far, through testings, has shown to be the optimal way.
*/
annotations = [[NSMutableArray alloc] init];
bool needsReload = NO;
//Loop through all locations parsed from the DB
for (int i = 0; i < [[[[self appDelegate] rssParser]rssItems] count]; i++)
NSString *temp = [NSString stringWithFormat:@"%@, %@, %@", [[[[[self appDelegate]rssParser]rssItems] objectAtIndex:i] Address], [[[[[self appDelegate] rssParser]rssItems]objectAtIndex:i]City], [[[[[self appDelegate] rssParser]rssItems]objectAtIndex:i]State]];
CLLocationCoordinate2D tempLocation = [self addressLocation:temp];
if (tempLocation.latitude == 0.0)
//We have been rate banned and flag for a re-run through locations.
[[[[[self appDelegate] rssParser]rssItems]objectAtIndex:i] setLocation:nil];
needsReload = YES;
[annotations addObject:@""];
else
/*
Set location of property. Done for property use in other locations of app.
*/
CLLocation *tempLoc = [[CLLocation alloc] initWithLatitude:tempLocation.latitude longitude:tempLocation.longitude];
[[[[[self appDelegate] rssParser]rssItems]objectAtIndex:i] setLocation:tempLoc];
//Create new annotation with proper coordinates
MapAnnotation *addAnnotation = [[MapAnnotation alloc] initWithCoordinate:tempLocation];
addAnnotation.name = [[[[[self appDelegate] rssParser]rssItems]objectAtIndex:i]Address];
// Check to see if location has an image associated with it.
if ([[[[[[self appDelegate] rssParser]rssItems]objectAtIndex:i] imageURLs] count] > 0 )
addAnnotation.thumbURL = [[[[[[self appDelegate] rssParser]rssItems]objectAtIndex:i] imageURLs] objectAtIndex:0];
else
addAnnotation.thumbURL = nil;
addAnnotation.tag = i;
addAnnotation.description = [NSString stringWithFormat:@"%@, %@ | $%@",[[[[[self appDelegate] rssParser]rssItems]objectAtIndex:i]City] , [[[[[self appDelegate] rssParser]rssItems]objectAtIndex:i]State] ,[[[[[[self appDelegate]rssParser]rssItems] objectAtIndex:i] Deposit] stringByReplacingOccurrencesOfString:@".00" withString:@""]];
//Add annotation to "annotations" array for fast addition/removal when filtering.
[annotations addObject:addAnnotation];
[self._mapView addAnnotation:addAnnotation];
//Doesn't seem necessary in ARC but somehow helps with memory on iPhone 3GS
temp = nil;
addAnnotation = nil;
//Attempt to force a refresh so annotations show up.
[self._mapView setCenterCoordinate:self._mapView.centerCoordinate zoomLevel:ZOOM_LEVEL animated:NO];
/*
If there was a rate ban we need to run through the locations again to get proper coordinates
*/
if (needsReload == YES)
//Sleep the thread to shake off rate ban from google.
[NSThread sleepForTimeInterval:2.0f];
//Move to the background so user can begin interacting with app
[self performSelectorInBackground:@selector(rateLimited) withObject:nil];
else
//All locations were property loaded so we can stop here.
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
self.radiusControl.hidden = NO;
如果有速率禁令,我们将移至递归调用方法以正确获取所有坐标。
- (void)rateLimited
/*
Create an array to hold the newly loaded annotations. For some reason the map will only load annoations if I add this this way. Instead of adding them one at a time as done before. I suppose it has to do with some refresh issue.
*/
NSMutableArray *tempReload = [[NSMutableArray alloc] init];
bool needsReload = NO;
/*
Run through the parsed list of property objects again looking for @""
*/
for ( int i = 0; i < [annotations count]; i++)
if ([[annotations objectAtIndex:i] isEqualToString:@""])
Property *tempProp = (Property*) [[[self.appDelegate rssParser] rssItems] objectAtIndex:i];
NSString *temp = [NSString stringWithFormat:@"%@, %@, %@", [tempProp Address], [tempProp City], [tempProp State]];
CLLocationCoordinate2D tempLocation = [self addressLocation:temp];
//There was another rate ban so we need to run through list again.
if (tempLocation.latitude == 0.0)
needsReload = YES;
else
CLLocation *tempLoc = [[CLLocation alloc] initWithLatitude:tempLocation.latitude longitude:tempLocation.longitude];
[[[[[self appDelegate] rssParser]rssItems]objectAtIndex:i] setLocation:tempLoc];
MapAnnotation *addAnnotation = [[MapAnnotation alloc] initWithCoordinate:tempLocation];
addAnnotation.name = [tempProp Address];
if ([[tempProp imageURLs] count] > 0 )
addAnnotation.thumbURL = [[tempProp imageURLs] objectAtIndex:0];
else
addAnnotation.thumbURL = nil;
//Have to keep tags corresponding to the inital listLocation assigned when DB is parsed from server. Done so when properties are added/removed they can be referenced correctly.
addAnnotation.tag = [tempProp listLocation];
//Set description
addAnnotation.description = [NSString stringWithFormat:@"%@, %@ | $%@",[tempProp City] , [tempProp State] ,[[tempProp Deposit] stringByReplacingOccurrencesOfString:@".00" withString:@""]];
//Repace the @"" with the proper annotation.
[annotations replaceObjectAtIndex:i withObject:addAnnotation];
//Add annotation for bulk addition after loop as finished.
[tempReload addObject:addAnnotation];
temp = nil;
addAnnotation = nil;
//Bulk add new annoations
[self._mapView addAnnotations:tempReload];
//If we need to reload, we recusivly call this method until all locations have proper coordinates.
if (needsReload == YES)
needsReload = NO;
tempReload = nil;
//The recusive call
[self rateLimited];
else
//All locations have property loaded now we can finish. *wew*
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
self.radiusControl.hidden = NO;
这是我的反向地理编码方法,也许有人可能有更好的方法。
-(CLLocationCoordinate2D)addressLocation:(NSString*)_address
NSString *urlString = [NSString stringWithFormat:@"http://maps.google.com/maps/geo?q=%@&output=csv",
[_address stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSError* error = nil;
NSString *locationString = [NSString stringWithContentsOfURL:[NSURL URLWithString:urlString ] encoding:NSASCIIStringEncoding error:&error];
NSArray *listItems = [locationString componentsSeparatedByString:@","];
double latitude = 0.0;
double longitude = 0.0;
if([listItems count] >= 4 && [[listItems objectAtIndex:0] isEqualToString:@"200"])
latitude = [[listItems objectAtIndex:2] doubleValue];
longitude = [[listItems objectAtIndex:3] doubleValue];
else
NSLog(@"error");
CLLocationCoordinate2D location;
location.latitude = latitude;
location.longitude = longitude;
return location;
【问题讨论】:
【参考方案1】:我会将每个位置设置为自己的对象,然后让该对象自己负责确定和设置自己的位置。 (如果出现问题,会在稍许延迟后重试)
然后您可以根据需要创建它们并让它们做自己的事情 - 仅在它们让您知道它们已准备好时才将它们显示在您的地图上。
【讨论】:
以上是关于向 MKMapView 添加注释的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章
如何在 MKMapView 上保存 MKAnnotations?