Obj-C - 所有自定义 MKMapView 注释并不总是显示在 MapView 上?

Obj-C - All Custom MKMapView annotations not always showing on MapView?

我正在尝试使用以下代码(餐厅、公园、聚会和商店)在我的 mapView 上显示多个位置数组。当我打开应用程序时,并非数组内的所有地址都显示在我的 mapView 上(即使返回了所有数据) - 例如有时只有 'Stores' 和 'Parks' 注释数组会显示在我的地图上,但餐厅不可见(即使所有数组的数据都已成功返回)。如果我关闭并重新打开应用程序,有时公园会显示在地图上,但不会显示其他内容。知道为什么会发生这种情况,我该如何解决?代码更新如下。一直在这!

注:ParksAnnotation, RestAnnotation, meetupAnn, & StoreAnnotation 都是MKPointAnnotation 类.

更新 (10/13/2020): 我尝试在检索和地理编码我的 'Stores' 坐标的代码块下记录 'placemarks'。看起来好像 self.storeData 已填充,创建的 NSDictionary storeFields 也是如此。也就是说,当我记录 'placemarks' 时,它不会返回任何坐标,即使 storeFields[@"address"] 已填充。其他代码块似乎工作得很好(即检索 Meet Ups 和检索 Parks)。因此 Meet Ups、Restaurants 和 Parks 注释看起来很好,但所有 Stores 注释都没有填充。当我启动应用程序时,至少一种类型的注释(有时是缺少商店,有时是公园)随机发生这种情况。我一辈子都弄不明白为什么会这样。

见下面代码:

MapViewController.m

#import "StoreAnnotation.h"
#import "ParksAnnotation.h"
#import "RestAnnotation.h"
#import "meetupAnn.h"


@interface MapViewController ()

@end

@implementation MapViewController


      -(void)viewWillAppear:(BOOL)animated {
            

               NSMutableDictionary *viewParamsallUsers1 = [NSMutableDictionary new];
               [viewParamsallUsers1 setValue:@"hosted_meet_ups" forKey:@"view_name"];
               [DIOSView viewGet:viewParamsallUsers1 success:^(AFHTTPRequestOperation *operation, id responseObject) {
                   
                   self.meetUps = [responseObject mutableCopy];
                  
                   int index = 0;
                   
                   
                   for (NSMutableDictionary *allMeetups in self.meetUps) {
                       
                       NSString *location = allMeetups[@"where"];
                       NSString *userNames = allMeetups[@"node_title"];
                       NSString *userBio = allMeetups[@"body"];
                    
      
                       self.alertMessage = [allMeetups[@"node_title"] mutableCopy];
                       
                  
                       CLGeocoder *geocoderFriend = [[CLGeocoder alloc] init];
                       [geocoderFriend geocodeAddressString:location
                                          completionHandler:^(NSArray* placemarks, NSError* error){
                                              if (placemarks && placemarks.count > 0) {
                                                  CLPlacemark *topResult = [placemarks objectAtIndex:0];
                                                  MKPlacemark *placemark = [[MKPlacemark alloc] initWithPlacemark:topResult];
                                                  
                                                  MKCoordinateRegion region = self.mapView.region;
                                                  
                                                  region.span.longitudeDelta /= 150.0;
                                                  region.span.latitudeDelta /= 150.0;
                                                  
                                                  
                                                  meetupAnn *meet = [[meetupAnn alloc] init];
                                                  meet.coordinate = placemark.coordinate;
                                                  meet.title = userNames;
                                                  meet.subtitle = userBio;
                                                  meet.index = index;  // Store index here.
                                                  
                                                  [self.mapView addAnnotation:meet];
                                              }
                                          }
                        ];
                       
                       index = index + 1;
        
                   }
                
               
                   
               } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                   NSLog(@"Failure: %@", [error localizedDescription]);
               }];
           
        
            
        
            
            /// GRAB ALL RESTAURANT LOCATIONS ///
            
            NSMutableDictionary *viewParams6 = [NSMutableDictionary new];
            [viewParams6 setValue:@"restaurants" forKey:@"view_name"];
            [DIOSView viewGet:viewParams6 success:^(AFHTTPRequestOperation *operation, id responseObject) {
                
                self.neighbourhoodData = [responseObject mutableCopy];
              
                int index = 0;
                
                
                for (NSMutableDictionary *multiplelocationsFriend in self.neighbourhoodData) {
                    
                
                    NSString *location = multiplelocationsFriend[@"address"];
                    NSString *userNames = multiplelocationsFriend[@"node_title"];
                     NSString *ampRemoved = [userNames stringByReplacingOccurrencesOfString:@"amp;" withString:@""];
                    
                    NSString *userBio = multiplelocationsFriend[@"body"];
                    self.x3 = multiplelocationsFriend[@"x3"];
             
                    
                    CLGeocoder *geocoderFriend = [[CLGeocoder alloc] init];
                    [geocoderFriend geocodeAddressString:location
                                       completionHandler:^(NSArray* placemarks, NSError* error){
                                           if (placemarks && placemarks.count > 0) {
                                               CLPlacemark *topResult = [placemarks objectAtIndex:0];
                                               MKPlacemark *placemark = [[MKPlacemark alloc] initWithPlacemark:topResult];
                                               
                                               MKCoordinateRegion region = self.mapView.region;
                                               
                                               region.span.longitudeDelta /= 150.0;
                                               region.span.latitudeDelta /= 150.0;
                                               
                                               
                                               RestAnnotation *point1 = [[RestAnnotation alloc] init];
                                               point1.coordinate = placemark.coordinate;
                                               point1.title = ampRemoved;
                                               point1.subtitle = userBio;
                                              
                                               point1.index = index;  // Store index here.
                                              
                                               
                                               [self.mapView addAnnotation:point1];
                                           }
                                       }
                     ];
                    
                    index = index + 1;
              
                }
         
                
            } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                NSLog(@"Failure: %@", [error localizedDescription]);
            }];
            
            
        
            
            NSString *test = self.areaName;
            NSLog(@"SHOW TEST %@", test);
            
            
              /// GRAB ALL STORE LOCATIONS ///
               
               NSMutableDictionary *viewParams7 = [NSMutableDictionary new];
               [viewParams7 setValue:@"stores" forKey:@"view_name"];
               [DIOSView viewGet:viewParams7 success:^(AFHTTPRequestOperation *operation, id responseObject) {
                   
                   self.storesData = [responseObject mutableCopy];
                 
                   int index = 0;
                   
                   
                   for (NSMutableDictionary *storeFields in self.storesData) {
                       
                    
                       NSString *location = storeFields[@"address"];
                       NSString *userNames = storeFields[@"node_title"];
                        NSString *ampRemoved = [userNames stringByReplacingOccurrencesOfString:@"amp;" withString:@""];
                       
                       NSString *userBio = storeFields[@"body"];
                       self.x3 = storeFields[@"x3"];
                
                       
                       CLGeocoder *geocoderFriend = [[CLGeocoder alloc] init];
                       [geocoderFriend geocodeAddressString:location
                                          completionHandler:^(NSArray* placemarks, NSError* error){
                                              if (placemarks && placemarks.count > 0) {
                                                  CLPlacemark *topResult = [placemarks objectAtIndex:0];
                                                  MKPlacemark *placemark = [[MKPlacemark alloc] initWithPlacemark:topResult];
                                                  
                                                  MKCoordinateRegion region = self.mapView.region;
                                                  
                                                  region.span.longitudeDelta /= 150.0;
                                                  region.span.latitudeDelta /= 150.0;
                                                  
                                                  
                                                  StoreAnnotation *point2 = [[StoreAnnotation alloc] init];
                                                  point2.coordinate = placemark.coordinate;
                                                  point2.title = ampRemoved;
                                                  point2.subtitle = userBio;
                                                 
                                                  point2.index = index;  // Store index here.
                                                 
                                                  
                                                  [self.mapView addAnnotation:point2];
                                                  
                                               
                                                  
                                              }
                                          }
                        ];
                       
                       index = index + 1;
             
                   }
            
                   
               } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                   NSLog(@"Failure: %@", [error localizedDescription]);
               }];
               
               
            
                    /// GRAB ALL PARKS LOCATIONS ///
                         
                         NSMutableDictionary *viewParams8 = [NSMutableDictionary new];
                         [viewParams8 setValue:@"parks" forKey:@"view_name"];
                         [DIOSView viewGet:viewParams8 success:^(AFHTTPRequestOperation *operation, id responseObject) {
                             
                             self.parksData = [responseObject mutableCopy];
                          
                             
                             int index = 0;
                             
                             
                             for (NSMutableDictionary *parkFields in self.parksData) {
                                 
                                 //  NSLog(@"WHAT IS IN FRIENDS %@", self.friendData);
                                 
                                 NSString *location = parkFields[@"address"];
                                 NSString *userNames = parkFields[@"node_title"];
                                  NSString *ampRemoved = [userNames stringByReplacingOccurrencesOfString:@"amp;" withString:@""];
                                 
                                 NSString *userBio = parkFields[@"body"];
                                 self.x3 = parkFields[@"x3"];
                          
                                 
                                 CLGeocoder *geocoderFriend = [[CLGeocoder alloc] init];
                                 [geocoderFriend geocodeAddressString:location
                                                    completionHandler:^(NSArray* placemarks, NSError* error){
                                                        if (placemarks && placemarks.count > 0) {
                                                            CLPlacemark *topResult = [placemarks objectAtIndex:0];
                                                            MKPlacemark *placemark = [[MKPlacemark alloc] initWithPlacemark:topResult];
                                                            
                                                            MKCoordinateRegion region = self.mapView.region;
                                                            
                                                            region.span.longitudeDelta /= 150.0;
                                                            region.span.latitudeDelta /= 150.0;
                                                            
                                                            
                                                            ParksAnnotation *point3 = [[ParksAnnotation alloc] init];
                                                            point3.coordinate = placemark.coordinate;
                                                            point3.title = ampRemoved;
                                                            point3.subtitle = userBio;
                                                           
                                                            point3.index = index;  // Store index here.
                                                           
                                                            
                                                            [self.mapView addAnnotation:point3];
                                                            
                                                            
                                                         
                                                           
                                                        }
                                                    }
                                  ];
                                 
                                 index = index + 1;
                              
        
                             }
                       
                            
                         } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                             NSLog(@"Failure: %@", [error localizedDescription]);
                         }];
                    
            }
        
          - (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
    {
    
       static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{
        
       MKCoordinateRegion mapRegion;
        mapRegion.center = mapView.userLocation.coordinate;
        mapRegion.span.latitudeDelta = 0.5;
        mapRegion.span.longitudeDelta = 0.5;
        
        [mapView setRegion:mapRegion animated: YES];
        
        [self.locationManager stopUpdatingLocation];
        self.locationManager = nil;
    
            MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(userLocation.coordinate, 8000, 8000);
           [mapView setRegion:[mapView regionThatFits:region] animated:YES];
            
           [mapView addAnnotations:[mapView annotations]];
    
            
           });
    }
    
        - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation{
           
         
            if([annotation isKindOfClass:[StoreAnnotation class]]) {
                static NSString *identifier = @"stores";
                MKAnnotationView *storesView = (MKAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
                
                if(storesView == nil) {
                    
                   
                   storesView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
                    storesView.displayPriority = MKFeatureDisplayPriorityRequired;
                  
                   storesView.canShowCallout = YES;
                    storesView.image = [UIImage imageNamed:@"storeann4.png"];
                    
                  
                }
                
               else  {
                    
                   storesView.annotation = annotation;
                
          
            }
              
                return storesView;
               
            }
            
            if([annotation isKindOfClass:[meetupAnn class]]) {
                static NSString *identifier = @"meetUps";
                MKAnnotationView *pulsingView = (MKAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
                
                if(pulsingView == nil) {
                    
                    pulsingView.displayPriority = MKFeatureDisplayPriorityRequired;
                    pulsingView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
                    pulsingView.image = [UIImage imageNamed:@"meetupbeacon.png"];
                    pulsingView.canShowCallout = YES;
               
               }
                
                else  {
                
                 pulsingView.annotation = annotation;
             
            }
              
                return pulsingView;
               
            }
                
                if([annotation isKindOfClass:[ParksAnnotation class]]) {
                    static NSString *identifier = @"parks";
                    MKAnnotationView *parksView = (MKAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
                    
                    if(parksView == nil) {
                        
                      
                       parksView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
                        parksView.displayPriority = MKFeatureDisplayPriorityRequired;
                       parksView.image = [UIImage imageNamed:@"parksann.png"];
                        parksView.canShowCallout = YES;
                   
                    }
                    
                   else  {
                        
                        
                        parksView.annotation = annotation;
               
                }
                  
                    return parksView;
                   
                }
            
                if([annotation isKindOfClass:[RestAnnotation class]]) {
                    static NSString *identifier = @"rests";
                    MKAnnotationView *restView = (MKAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
                    
                    if(restView == nil) {
            
                       restView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
                        restView.displayPriority = MKFeatureDisplayPriorityRequired;
                       restView.image = [UIImage imageNamed:@"restann.png"];
                       restView.canShowCallout = YES;
                   
                    }
                    
                else  {
                        
                        
                        restView.annotation = annotation;
                   
                }
                  
                    return restView;
                   
                }

您正在将 MKFeatureDisplayPriorityRequired 分配给空对象。尝试在两种情况下分配显示优先级:

  1. 当pulsingView不为nil时

  2. pulsingView 分配后。

    if([annotation isKindOfClass:[meetupAnn class]]) {
         static NSString *identifier = @"currentLocation";
         MKAnnotationView *pulsingView = (MKAnnotationView *)[self.friendsMapView dequeueReusableAnnotationViewWithIdentifier:identifier];
    
         if(pulsingView == nil) {
             pulsingView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
             pulsingView.displayPriority = MKFeatureDisplayPriorityRequired;
             pulsingView.canShowCallout = YES;
             pulsingView.image = [UIImage imageNamed:@"meetupbeacon.png"];
             NSLog(@"Location Returned");
         } else {
             pulsingView.displayPriority = MKFeatureDisplayPriorityRequired;
             // TODO: Do map pin initial setup.
         }
    }
    

正如 Ramis 所说,问题在于您甚至在实例化 pulsingView 之前就设置了 displayPriority。因此,您随后实例化的 MKAnnotationView 从未得到其 displayPriority 集。

话虽如此,但我建议采用略有不同的实施方式。具体来说,我会将注释视图的配置移动到它自己的子 class 中,而不是用这种代码使视图控制器混乱:

@interface MeetUpAnnotationView: MKAnnotationView
@end

@implementation MeetUpAnnotationView

- (instancetype)initWithAnnotation:(id<MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
    if (self) {
        self.displayPriority = MKFeatureDisplayPriorityRequired;
        self.canShowCallout = YES;
        self.image = [UIImage imageNamed:@"meetupbeacon.png"];
    }
    return self;
}

- (void)setAnnotation:(id<MKAnnotation>)annotation {
    [super setAnnotation:annotation];
    self.displayPriority = MKFeatureDisplayPriorityRequired;
}

@end

然后,如果目标是 iOS 11 或更高版本,您的视图控制器的 viewDidLoad 应该注册该子class。

现在,如果这是您在 iOS 11 中显示的唯一注释视图,则您根本不需要 viewForAnnotation,只需注册您的默认注释视图即可:

[self.mapView registerClass:[MeetUpAnnotationView class] forAnnotationViewWithReuseIdentifier:MKMapViewDefaultAnnotationViewReuseIdentifier];

并且注册了注释视图 class,您就完成了。不需要或不需要 viewForAnnotation


您唯一需要在 iOS 11 及更高版本中实施 viewForAnnotation 的情况是您的地图上有多个自定义注释视图类型。在这种情况下,您需要注册它们:

[self.mapView registerClass:[MeetUpAnnotationView class] forAnnotationViewWithReuseIdentifier:meetupIdentifier];
[self.mapView registerClass:[SomeOtherAnnotationView class] forAnnotationViewWithReuseIdentifier:someOtherIdentifier];

然后在 viewForAnnotation:

中使用 dequeueReusableAnnotationViewWithIdentifier:forAnnotation:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
    if ([annotation isKindOfClass:[MeetupAnn class]]) {
        return [mapView dequeueReusableAnnotationViewWithIdentifier:meetupIdentifier forAnnotation:annotation];
    }

    if ([annotation isKindOfClass:[SomeOtherAnnotation class]]) {
        return [mapView dequeueReusableAnnotationViewWithIdentifier:someOtherIdentifier forAnnotation:annotation];
    }

    ...

    return nil;
}

如您所见,在iOS11中(尤其是当您只有一种注释视图类型时),代码大大简化了。

但是如果你需要支持旧的iOS版本,你不能注册标识符,你必须使用旧的dequeueReusableAnnotationViewWithIdentifier:,但我还是让注解视图子class 自行配置。但是,代码中的一个更微妙的问题是您没有在 else 子句中设置 annotation,因此请确保这样做,例如:

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
    if ([annotation isKindOfClass:[MeetupAnn class]]) {
        MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:meetupIdentifier];
        if (!annotationView) {
            annotationView = [[MeetUpAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:meetupIdentifier];
        } else {
            annotationView.annotation = annotation;
        }
        return annotationView;
    }

    return nil;
}

您应该检查您的某些地理编码请求是否不一致地失败。 CLGeocoder 速率限制您的请求,如果您在短时间内请求太多,您将收到错误消息。 https://developer.apple.com/documentation/corelocation/clgeocoder/1423509-geocodeaddressstring?language=objc

检查在 completionHandler 块内 NSError* error 内是否有任何失败原因。

失败的次数越多,您在 MapView 上看到的注释就越少,因为它没有输入以下代码路径。

if (placemarks && placemarks.count > 0) {
    // Not entering here
}

if (nil != error) {
    // Could land here
}

唯一的其他原因是没有正确计算地图区域以在屏幕上显示所有注释。确保在将每个注释添加到 mapView 时 calculating/adjusting 区域正确。

  // Define these variables globally in the view controller
  CLLocationDegrees minLatitude = 90.0;
  CLLocationDegrees maxLatitude = -90.0;
  CLLocationDegrees minLongitude = 180.0;
  CLLocationDegrees maxLongitude = -180.0;
  

 // Call following method every time a new annotation needs to be added to the mapView
 -(void)addAnnotation:(id<MKAnnotation>)annotation toMapView:(MKMapView*)mapView {
      // Add the annotation to map
      [mapView addAnnotation:annotation];
       
      // Set the map region to make it visible along with all other annotations
      CLLocationDegrees latitude = annotation.coordinate.latitude;
      CLLocationDegrees longitude = annotation.coordinate.longitude;
      
      minLatitude = min(minLatitude, latitude);
      maxLatitude = max(maxLatitude, latitude);
      minLongitude = min(minLongitude, longitude);
      maxLongitude = max(maxLongitude, longitude);

      CLLocationDegrees latitudeDelta = (maxLatitude - minLatitude);
      CLLocationDegrees longitudeDelta = (maxLongitude - minLongitude);
      
      CLLocationDegrees midLatitude = (maxLatitude - latitudeDelta/2);
      CLLocationDegrees midLongitude = (maxLongitude - longitudeDelta/2);
      CLLocationCoordinate2D center = CLLocationCoordinate2DMake(midLatitude, midLongitude);

      MKCoordinateSpan span = MKCoordinateSpanMake(latitudeDelta, longitudeDelta);
      MKCoordinateRegion region = MKCoordinateRegionMake(center, span);
      
      if (CLLocationCoordinate2DIsValid(center)) {
          [mapView setRegion:region animated:YES];
      }
 }