向 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 上显示自定义注释?

MKMapView 和注释隐藏与缩放

如何在 MKMapView 上保存 MKAnnotations?

如何从 MKMapView didSelect 注释更新封装的 SwiftUI 视图

如果在添加注释时缩放,MKMapView 会崩溃

如何在 MKMapView 上添加的自定义注解中添加标题和副标题?