How do I show multiple custom annotations (pin & left icon) loaded via plist?

后端 未结 2 1036
[愿得一人]
[愿得一人] 2021-02-11 09:27

will this code when working show custom pins and logos or just 1 pin with custom logos? please see plist below, only error is Local declaration of mapView hides instance variabl

相关标签:
2条回答
  • 2021-02-11 10:02

    Add a stationIdKey property to your custom annotation class MyAnnotation and set the property when creating the annotation before calling addAnnotation. For example:

    //in MyAnnotation.h:
    @property (nonatomic, copy) NSString *stationIdKey; //and release in dealloc
    
    //in viewDidLoad before calling addAnnotation:
    myAnnotation.stationIdKey = [note objectForKey:@"stationIdKey"];
    

    Then in viewForAnnotation, cast annotation to the custom class and set the icon based on the stationIdKey property:

    NSString *iconFilename = @"";
    MyAnnotation *myAnn = (MyAnnotation *)annotation;
    if ([myAnn.stationIdKey isEqualToString:@"BP"])
        iconFilename = @"bp-logo.png";
    else
        if ([myAnn.stationIdKey isEqualToString:@"Caltex"])
            iconFilename = @"caltex.png";
        else
            if ([myAnn.stationIdKey isEqualToString:@"Shell"])
                iconFilename = @"shell.png";
    UIImageView *leftIconView = [[[UIImageView alloc] 
        initWithImage:[UIImage imageNamed:iconFilename]] autorelease];
    


    (There are a few other issues with the code which I'll add details about later.)


    Edit:
    Sorry, I wasn't clear that the code shown above for viewForAnnotation is only supposed to replace the part of the existing code that sets the leftCalloutAccessoryView. It's not the whole delegate method.

    The warning Control reaches end of non-void function means that the replacement code is not returning a value even though the method is declared as returning a MKAnnotationView *.

    Below is the full method with other unrelated suggested changes:

    -(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
    {
        if ([annotation isKindOfClass:[MyAnnotation class]])
        {
            static NSString *reuseId = @"customAnn";
    
            MKAnnotationView *customAnnotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:reuseId];
            if (customAnnotationView == nil)
            {
                customAnnotationView = [[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseId] autorelease];
                UIImage *pinImage = [UIImage imageNamed:@"pin-green.png"];
                [customAnnotationView setImage:pinImage];
                customAnnotationView.canShowCallout = YES;
                UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
                customAnnotationView.rightCalloutAccessoryView = rightButton;
            }
    
            NSString *iconFilename = @"";
            MyAnnotation *myAnn = (MyAnnotation *)annotation;
            if ([myAnn.stationIdKey isEqualToString:@"BP"])
                iconFilename = @"bp-logo.png";
            else
                if ([myAnn.stationIdKey isEqualToString:@"Caltex"])
                    iconFilename = @"caltex.png";
                else
                    if ([myAnn.stationIdKey isEqualToString:@"Shell"])
                        iconFilename = @"shell.png";
            UIImageView *leftIconView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:iconFilename]] autorelease];
            customAnnotationView.leftCalloutAccessoryView = leftIconView;
    
            customAnnotationView.annotation = annotation;
    
            return customAnnotationView; 
        }
    
        return nil;
    }
    

    I've added the following:

    • Only create a custom annotation view if the annotation type is MyAnnotation otherwise return nil. So if you decide later to show other types of annotations like user location, it will still work.
    • Implemented annotation view re-use by calling the dequeue method. This can improve performance if you have a lot of annotations.
    • Moved the setting of annotation view properties that don't change per annotation to only when the view is newly created (and not being re-used).
    • Removed the addTarget:action: call for the rightButton. I highly recommend you implement the map view's calloutAccessoryControlTapped delegate method instead of a custom method. So instead of an annotationViewClick: method, implement the delegate method, like this:

      -(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
      {
          if ([view.annotation isKindOfClass:[MyAnnotation class]])
          {
              MyAnnotation *myAnn = (MyAnnotation *)view.annotation;
              NSLog(@"callout button tapped for station id %@", myAnn.stationIdKey);
          }
          else
          {
              NSLog(@"callout button tapped for annotation %@", view.annotation);
          }
      }
      


    Finally, in viewDidLoad, you are creating the anns array using alloc but you are not calling release which results in a memory leak.

    Either replace the alloc+init of the anns array with this auto-release version (recommended):

    NSArray *anns = [NSArray arrayWithContentsOfFile:path];
    

    or add this after the for-loop right before the end of the viewDidLoad method:

    [anns release];
    
    0 讨论(0)
  • 2021-02-11 10:14

    (Posting a separate answer for the slightly modified question. The original question was how to change the annotation callout image based on some property of the annotation object. The modified question is why the annotations are not appearing at all based on the latest code and how to fix the warning in viewForAnnotation.)

    The only obvious problem with the latest code in the question is that the viewDidLoad method is spelled wrong which prevents that code from getting called at all. Please change this:

    - (void)viewDidload  //the lowercase "l" is not correct, must be uppercase
    

    to this:

    - (void)viewDidLoad
    


    A few other things to check just in case:

    • Plist file should really be like <?xml ...><!DOCTYPE ...><plist ...><array><dict>xxx</dict><dict>yyy</dict><dict>zzz</dict></array></plist>
    • Make sure the image files like pin-green.png, etc have been added to the project and are named exactly that way (uppercase/lowercase matters on the device)

    Next, to fix the compiler warning in viewForAnnotation, change the name of the mapView parameter to something else so it's different from the mapView ivar in the view controller. Following example changes it to aMapView (two places marked with ^^^^^^):

    -(MKAnnotationView *)mapView:(MKMapView *)aMapView viewForAnnotation:(id<MKAnnotation>)annotation
                                              ^^^^^^^^
    {  
        if ([annotation isKindOfClass:[MyAnnotation class]])
        {
            static NSString *reuseId = @"customAnn";
    
            MKAnnotationView *customAnnotationView = [aMapView dequeueReusableAnnotationViewWithIdentifier:reuseId];
                                                      ^^^^^^^^
            if (customAnnotationView == nil)
            {
                customAnnotationView = [[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseId] autorelease];
                UIImage *pinImage = [UIImage imageNamed:@"pin-green.png"];
                [customAnnotationView setImage:pinImage];
                customAnnotationView.canShowCallout = YES;
                UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
                customAnnotationView.rightCalloutAccessoryView = rightButton;
            }
    
            NSString *iconFilename = @"";
            MyAnnotation *myAnn = (MyAnnotation *)annotation;
            if ([myAnn.stationIdKey isEqualToString:@"BP"])
                iconFilename = @"bp-logo.png";
            else
                if ([myAnn.stationIdKey isEqualToString:@"Caltex"])
                    iconFilename = @"caltex.png";
                else
                    if ([myAnn.stationIdKey isEqualToString:@"Shell"])
                        iconFilename = @"shell.png";
            UIImageView *leftIconView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:iconFilename]] autorelease];
            customAnnotationView.leftCalloutAccessoryView = leftIconView;
    
            customAnnotationView.annotation = annotation;
    
            return customAnnotationView; 
        }
    
        return nil; 
    }
    


    This is how it looks (your pin and callout images may be different):

    Green Pins

    Shell Callout

    Caltex Callout

    BP Callout

    If you want the pins themselves to have different images instead of their callouts, then in viewForAnnotation, call setImage instead of setting the leftCalloutAccessoryView.


    Another minor, unrelated, thing is that you don't need to call mapView setDelegate inside the for-loop. You only need to call it once before the for-loop (and if you've already connected the map view's delegate to File's Owner in the xib, you don't need to do it in code at all).

    0 讨论(0)
提交回复
热议问题