简体   繁体   中英

Rotate annotation image on MKMapView

I am trying to rotate an image that is added to MKMapView as an annotation.

This is the code:

-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:   (id<MKAnnotation>)annotation
{
  if (! [annotation isKindOfClass:[IGAMapAnnotation class]])
  {
    //return default view if annotation is NOT of type IGAMapAnnotation...
    return nil;
  }


  MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:@"IGAMapAnnotation"];

  if (annotationView == nil)
  {
    annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"IGAMapAnnotation"];

    annotationView.enabled = YES;
    annotationView.canShowCallout = YES;
    annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
  }
  else
  {
    annotationView.annotation = annotation;
  }

  IGAMapAnnotation *myLocation = (IGAMapAnnotation *) annotation;

  // THIS IS IT!
  if ([myLocation.type isEqual: @"PLANE"]) {
    UIImage *planeImage = [UIImage imageNamed:@"planetracked.png"];

    UIImageView *planeImageView = [[UIImageView alloc]initWithImage:planeImage];
    planeImageView.transform = CGAffineTransformMakeRotation(M_PI_2);

    annotationView.image = planeImageView;
  }

  return annotationView;
}

It obviously gives me an error as annotationView.image should assign an image and not UIImageView. I have tried various methods rotating just an image, for example this:

- (UIImage *)rotateImage:(UIImage *)image onDegrees:(NSString *)heading {

  double angle = [heading doubleValue];

  CGSize s = {image.size.width, image.size.height};
  UIGraphicsBeginImageContext(s);
  CGContextRef ctx = UIGraphicsGetCurrentContext();
  CGContextTranslateCTM(ctx, 0,image.size.height);
  CGContextScaleCTM(ctx, 1.0, -1.0);

  CGContextRotateCTM(ctx, 2*M_PI*angle/360);
  CGContextDrawImage(ctx,CGRectMake(0,0,image.size.width, image.size.height),image.CGImage);
  UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();
  return newImage;
}

They do not work either -- no image appears on the map.

Anyone knows how to rotate an annotation image on MKMapView?

Million thanks!

Instead of:

annotationView.image = planeImageView;

which is definitely wrong (the image property is a UIImage while planeImageView is a UIImageView ), use addSubview: to add the UIImageView to the annotation view (leaving the view's image property nil and unused).

However, you'll also need to make some other adjustments so that:

  • The image is centered exactly on the coordinate (instead of its corner), and
  • Tapping anywhere on the image brings up the callout (instead of only one specific corner)

To do these things, increase the frame sizes of both views to account for the maximum width possible from a rotation (which is the square root of 2 times the original width assuming image is a square) and set the image view's contentMode to "center" so the image is not distorted by these frame size changes.

The other big issue is that if you have IGAMapAnnotation s whose type is not "PLANE", they will either be:

  • Invisible if a new annotation view is created (because image is not set nor is any subview added to the annotation view), or,
  • Showing a "plane" image with the heading of some other annotation because the annotation view was dequeued (and is being re-used for another annotation).

To avoid the two types of annotations ("plane"/"not plane") from re-using each other's views, I suggest using a different re-use identifier for each type ( not each annotation) and apply type-specific changes to the view.

The revised viewForAnnotation method would look like this:

-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
    if (! [annotation isKindOfClass:[IGAMapAnnotation class]])
    {
        //return default view if annotation is NOT of type IGAMapAnnotation...
        return nil;
    }

    IGAMapAnnotation *myLocation = (IGAMapAnnotation *)annotation;

    BOOL typeIsPlane = [myLocation.type isEqualToString:@"PLANE"];
    int planeImageViewTag = 42;

    NSString *reuseId = typeIsPlane ? @"IGAMapAnnotationPlane" : @"IGAMapAnnotationOther";

    MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:reuseId];

    if (annotationView == nil)
    {
        annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseId];
        annotationView.enabled = YES;
        annotationView.canShowCallout = YES;
        annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];

        if (typeIsPlane)
        {
            //Here, just add the image view to the annotation view with no
            //rotation.  Only want to add the image view to the annotation
            //view ONCE when the annotation view is initially created.  If
            //view is dequeued, it will already have an image view and we
            //just update its rotation.

            UIImage *planeImage = [UIImage imageNamed:@"planetracked.png"];

            UIImageView *planeImageView = [[UIImageView alloc] initWithImage:planeImage];
            planeImageView.tag = planeImageViewTag;
            planeImageView.contentMode = UIViewContentModeCenter;

            [annotationView addSubview: planeImageView];

            CGRect avFrame = annotationView.frame;
            //"1.5" on next line is the square root of 2 rounded up a bit.
            avFrame.size = CGSizeMake(planeImage.size.width*1.5, 
                                      planeImage.size.height*1.5);
            annotationView.frame = avFrame;
            planeImageView.frame = annotationView.frame;
        }
        else
        {
            //If this IGAMapAnnotation is not a "plane",
            //show some other default image.
            //(Or, you could return nil to show a default red pin.)

            annotationView.image = [UIImage imageNamed:@"NotAPlane.png"];

            //May or may not need to set centerOffset.
            //Either remove or adjust 0,0 as needed to 
            //center the image on the coordinates.
            annotationView.centerOffset = CGPointMake(0, 0);
        }
    }
    else
    {
        annotationView.annotation = annotation;
    }

    //At this point, we have a new or dequeued annotation view ready
    //and pointing to the current annotation.
    //Now make any annotation-specific changes to the view...

    if (typeIsPlane)
    {
        UIImageView *planeImageView = (UIImageView *)[annotationView viewWithTag:planeImageViewTag];
        planeImageView.transform = CGAffineTransformMakeRotation(M_PI_2);
        //Replace M_PI_2 with rotation specific to this annotation's heading.
    }

    return annotationView;
}

By the way, use isEqualToString: instead of isEqual: with NSString s.


For the removeAnnotations: problem, it must be that mapLocations contains new instances of the annotations on the map. To remove existing annotations, you have to provide a reference to the exact same objects that were added originally.

If you are always removing all annotations and re-adding all annotations, you can just do [self.mapView removeAnnotations:self.mapView.annotations]; .

If you are only removing some annotations, you'll need to keep references to the ones originally added or iterate through the map view's annotations array and identify which ones should be deleted (keep a temporary NSMutableArray as the list of "annotations to remove") and then call removeAnnotations: with that list of annotations to remove.

The following seems to work. Million thanks to Anna without whom it would not have!

-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {

  if (! [annotation isKindOfClass:[IGAMapAnnotation class]]) {
    return nil;
  }

  IGAMapAnnotation *myLocation = (IGAMapAnnotation *) annotation;

BOOL typeIsPlane = [myLocation.navaidType isEqualToString:@"PLANE"];
BOOL typeIsOne  = [myLocation.navaidType isEqualToString:@"ONE"];
BOOL typeIsTwo = [myLocation.navaidType isEqualToString:@"TWO"];
BOOL typeIsThree = [myLocation.navaidType isEqualToString:@"THREE"];

int planeImageViewTag = 42;

NSString *reuseId;
if (typeIsPlane)
    reuseId = @"IGAMapAnnotationPlane";

else if (typeIsOne)
    reuseId = @"IGAMapAnnotationOne";

else if (typeIsTwo)
    reuseId = @"IGAMapAnnotationTwo";

else if (typeIsThree)
    reuseId = @"IGAMapAnnotationThree";

else
    reuseId = @"IGAMapAnnotationOther";

MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:reuseId];

if (annotationView == nil)
{
    annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseId];
    annotationView.enabled = YES;
    annotationView.canShowCallout = YES;
    annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];

    if (typeIsPlane)
    {

        UIImage *planeImage = [UIImage imageNamed:@"mapPLANE.png"];

        UIImageView *planeImageView = [[UIImageView alloc] initWithImage:planeImage];
        planeImageView.tag = planeImageViewTag;
        planeImageView.contentMode = UIViewContentModeCenter;

        [annotationView addSubview: planeImageView];

        CGRect avFrame = annotationView.frame;
        //"1.5" on next line is the square root of 2 rounded up a bit.
        avFrame.size = CGSizeMake(planeImage.size.width*1.5,
                                  planeImage.size.height*1.5);
        annotationView.frame = avFrame;
        planeImageView.frame = annotationView.frame;
    }

    else if (typeIsOne)
    {
        annotationView.image = [UIImage imageNamed:@"one.png"];
        annotationView.centerOffset = CGPointMake(0, 0);
    }

    else if (typeIsTwo)
    {
        annotationView.image = [UIImage imageNamed:@"two.png"];
        annotationView.centerOffset = CGPointMake(0, 0);
    }

    else if (typeIsThree)
    {
        annotationView.image = [UIImage imageNamed:@"three.png"];
        annotationView.centerOffset = CGPointMake(0, 0);
    }

    else
        return nil;
}

else
{
    annotationView.annotation = annotation;
}

if (typeIsPlane)
{
    // Convert current heading string to double
    double headingDouble = [currentHeading doubleValue];

    UIImageView *planeImageView = (UIImageView *)[annotationView viewWithTag:planeImageViewTag];
    planeImageView.transform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(headingDouble));
}

return annotationView;

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM