【问题标题】:Obj-C - All Custom MKMapView annotations not always showing on MapView?Obj-C - 所有自定义 MKMapView 注释并不总是显示在 MapView 上?
【发布时间】:2020-09-17 01:20:55
【问题描述】:

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

注意:ParksAnnotation、RestAnnotation、meetupAnn 和 StoreAnnotation 都是 MKPointAnnotation 类。

更新(2020 年 10 月 13 日):我尝试在检索和地理编码我的“商店”坐标的代码块下记录“地标”。看起来好像 self.storeData 已填充,创建的 NSDictionary storeFields 也是如此。也就是说,当我记录“地标”时,即使填充了 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;
                   
                }

【问题讨论】:

  • 也许viewDidLoad 太早了?
  • @matt 还有什么选择? viewDidAppear?
  • 不知道,试试看。
  • 试过了 - 没有改变/没有改变。不过谢谢。 @马特
  • 我能想到的唯一其他事情是其中一些可能在屏幕外或重叠

标签: ios objective-c mkmapview mkannotation


【解决方案1】:

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

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

@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 应该注册该子类。

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

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

并且已经注册了注解视图类,你就完成了。不需要或不需要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;
}

如你所见,在 iOS 11 中(尤其是当你只有一种注解视图类型时),代码被大大简化了。

但是如果你需要支持旧的iOS版本,你不能注册标识符,你必须使用旧的dequeueReusableAnnotationViewWithIdentifier:,但我仍然让注解视图子类负责配置自己。但是,您的代码中一个更微妙的问题是您没有在 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;
}

【讨论】:

  • 我已经实现了上述方法,并且承认,它比我曾经拥有的要好得多。也就是说,我仍然有一些时候会启动,而我的“聚会”地点并没有显示在地图上:(
  • 注解是一种相当防弹的东西,我们一直都在使用它。所以也许你的坐标不对。也许地图视图插座没有正确连接。它可能有很多东西,但我敢打赌它与这个注释视图子类和相关代码无关。毫无疑问,这是另一回事。我们需要reproducible example 的问题。
  • 我能想到的一件事:在我上面的代码中,当我将 meetupAnn 添加到 self.friendsMapView 时,这一行:@property (nonatomic, retain) IBOutlet MKMapView *neighbourmapView; meetupAnn 是一个 MKAnnotationView。也就是说,该行向我发出警告:将'meetupAnn *__strong'发送到不兼容类型'id _Nonnull'的参数。我认为这是因为 meetupAnn 是 MKAnnotationView,而不是 MKAnnotationPoint?知道我应该/可以如何更改此行以修复警告,并查看它是否是我的问题的原因?谢谢罗!
  • 您通过addAnnotation 添加的对象必须是注解(例如MKPointAnnotation 或某个子类或其他符合MKAnnotation 的类),not 注解视图。上面,我假设MeetupAnn(我改变了它的大小写,因为类名总是以大写字母开头)是一个注释,而不是一个注释视图,因为将注释 view 传递给 @ 是没有意义的987654344@。注释是表示某些地理位置/名称的模型对象。注释视图是将在屏幕上呈现的视图。不要将两者混为一谈,因为它们完全不同。
  • 刚刚对我的代码进行了一些修改。我意识到注释并不总是显示的原因是因为我的数据检索得太晚了;即使在我的控制台中,它也是最后要返回的东西......只是不知道如何解决这个问题:/
【解决方案2】:

您应该检查您的某些地理编码请求是否不一致地失败。 CLGeocoder rate 会限制您的请求,如果您在短时间内发出过多请求,则会收到错误消息。 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 时正确计算/调整区域。

  // 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];
      }
 }

【讨论】:

  • 这非常有用。现在通过让我的后端进行地理编码来测试你的答案。很快就会告诉你进展如何。我想知道是否可能是这种情况,但是有大量应用程序可以将数百(或数千)个地理编码位置拉到地图视图上。
  • 哇,你成功了。这绝对是问题所在。太感谢了。我可以通过让我的数据库进行地理编码而不是让应用程序来解决这个问题。现在我的应用程序只是将完成的坐标从我的数据库中拉到一个数组中。你。摇滚。
【解决方案3】:

您将 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.
         }
    }
    

【讨论】:

  • @Rob 感谢刷新 Objective-c 内存。我确实相应地更新了我的答案。
  • 没问题。但是你仍然有一个问题,如果一个注释视图被重用(即pulsingView不是nil),你永远不会重置它的annotation属性。在else 子句中,添加pulsingView.annotation = annotation
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-17
  • 1970-01-01
相关资源
最近更新 更多