Rotate Annotation Custom Image [closed] - ios

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I am clearly not getting this. I'm trying to work out how to rotate a custom image of map annotations. i.e. multiple icons pointing different directions. I have loop'ed over a whole bunch of airplane data and I want to simply show the direction the plane is heading.
Review the code that I have managed to piece together to get at least half of it working and provide a suggestive idea on how to turn the image based on a variable.
Abstract View of the loop'ed data
NSMutableArray *locations = [[NSMutableArray alloc]init];
CLLocationCoordinate2D location;
MKPointAnnotation *myAnn;
myAnn = [[MKPointAnnotation alloc]init];
location.latitude = nLatitudeFloat;
location.longitude = nLongitudeFloat;
myAnn.coordinate = location;
myAnn.title = nRealname;
//Add the Annotation object to the list so when the map is presented all points are listed on the map.
[locations addObject:myAnn];
//[self.mapView setUserTrackingMode:MKUserTrackingModeFollow animated:YES];
[self.mapView addAnnotations:locations];
Update of the annotation:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
// If it's the user location, just return nil.
if ([annotation isKindOfClass:[MKUserLocation class]]) return nil;
// Handle any custom annotations.
if ([annotation isKindOfClass:[MKPointAnnotation class]])
{
// Try to dequeue an existing pin view first.
MKAnnotationView *pinView = (MKAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:#"CustomPinAnnotationView"];
if (!pinView)
{
// If an existing pin view was not available, create one.
pinView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"CustomPinAnnotationView"];
pinView.canShowCallout = YES;
pinView.image = [UIImage imageNamed:#"airplane21.png"] ;
pinView.calloutOffset = CGPointMake(0, 32);
pinView.transform = CGAffineTransformMakeRotation(30); <------AT THE MOMENT I HAVE JUST HARD CODED THIS TO SEE IF IT WORKS. IT ROTATES THE ENTIRE ANNOTATION
// Add a detail disclosure button to the callout.
//UIButton* rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
//pinView.rightCalloutAccessoryView = rightButton;
// Add an image to the left callout.
UIImageView *iconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"airplane21.png"]];
pinView.leftCalloutAccessoryView = iconView;
iconView.transform = CGAffineTransformMakeRotation(30);
} else {
pinView.annotation = annotation;
}
return pinView;
}
return nil;
}
Any ideas?
I have seen this by the way. I just wasn't confident to know if it would be suitable or not.
Rotate annotation image on MKMapView

I had the same problem and I have a solution that seems to work.
You need to first make a custom annotation that can hold the data that you want (ex. coordinates, your plane heading). When you loop through your data make sure you're using that custom annotation. For example
CustomAnnotation* annotation = [[CustomAnnotation alloc] init];
annotation.coordinates = ...
annotation.bearing = ...
Then in your viewForAnnotation method, you can get that info by doing something like
if ([annotation isKindOfClass:[CustomAnnotation class]]) {
CustomAnnotation* myAnn = (CustomAnnotation*)annotation;
double bearing = myAnn.bearing; // or whatever it's called
...
}
Hope this helped.
Edit: For rotating your image, I found the following code snippet somewhere. It works but it pixellates your image a little bit.
#interface UIImage (RotationMethods)
- (UIImage *)imageRotatedByDegrees:(CGFloat)degrees;
#end
#implementation UIImage (RotationMethods)
static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;};
- (UIImage *)imageRotatedByDegrees:(CGFloat)degrees
{
// calculate the size of the rotated view's containing box for our drawing space
UIView *rotatedViewBox = [[UIView alloc] initWithFrame:CGRectMake(0,0,self.size.width, self.size.height)];
CGAffineTransform t = CGAffineTransformMakeRotation(DegreesToRadians(degrees));
rotatedViewBox.transform = t;
CGSize rotatedSize = rotatedViewBox.frame.size;
// Create the bitmap context
//UIGraphicsBeginImageContext(rotatedSize); // For iOS < 4.0
UIGraphicsBeginImageContextWithOptions(rotatedSize, NO, 0.0);
CGContextRef bitmap = UIGraphicsGetCurrentContext();
// Move the origin to the middle of the image so we will rotate and scale around the center.
CGContextTranslateCTM(bitmap, rotatedSize.width/2, rotatedSize.height/2);
// Rotate the image context
CGContextRotateCTM(bitmap, DegreesToRadians(degrees));
// Now, draw the rotated/scaled image into the context
CGContextScaleCTM(bitmap, 1.0, -1.0);
CGContextDrawImage(bitmap, CGRectMake(-self.size.width / 2, -self.size.height / 2, self.size.width, self.size.height), [self CGImage]);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
#end
Stick that in your .m, and on any UIImage you can now do [someUIImage imageRotatedByDegrees:yourDegrees];
So now in your viewForAnnotation method you can do something like
UIImage* image = [[UIImage imageNamed:#"yourImage.png"] imageRotatedByDegrees:degrees];
annView.image = image;

Related

Draw Flag as Overlay on MKMapView

I want to draw a country's flag on top of country boundary. I am using the coordinate data from here
The way I am adding overlay is
[mapView addOverlays:countryName];
Then rendering it as
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
{
if ([overlay isKindOfClass:[MKPolygon class]])
{
MKPolygonRenderer *renderer = [[MKPolygonRenderer alloc] initWithPolygon:overlay];
renderer.fillColor = [ColorUtil colorFromHexString:#"#EE5A43" withAlpha:0.6];
renderer.strokeColor = [[UIColor whiteColor] colorWithAlphaComponent:1.0];
renderer.lineWidth = 2;
return renderer;
}
return nil;
}
Now my idea was to draw the overlay with the flag. So I subclasses MKPolygonRenderer and added
#implementation MyMKOverlayRenderer
- (instancetype)initWithOverlay:(id<MKOverlay>)overlay overlayImage:(UIImage *)overlayImage {
self = [super initWithOverlay:overlay];
if (self) {
_overlayImage = overlayImage;
}
return self;
}
- (void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context {
CGImageRef imageReference = self.overlayImage.CGImage;
MKMapRect theMapRect = self.overlay.boundingMapRect;
CGRect theRect = [self rectForMapRect:theMapRect];
CGContextScaleCTM(context, 1.0, -1.0);
CGContextTranslateCTM(context, 0.0, -theRect.size.height);
CGContextDrawImage(context, theRect, imageReference);
}
#end
Now check the two attached images. I was expecting the flag to be inside the boundary of the polygon. But looks like it is not happening. So may be my approach is wrong. Can some expert help me in correct direction.
I solved this problem. The key was to create a UIBezierPath and clip the overlay image.
I have posted the entire solution here
Now it looks like

How to add clicked annotations on mapbox using offline mbtiles files

I have already managed to use interactive offline mbtiles (created on TileMill) in order to:
Load more than 1000 points fast
Make them understand when user is clicking each point and show a popup with the name of each point
But I can't make the bubble with the name clickable again.
I use the following code to generate the layer for the annotation of each point
- (RMMapLayer *)mapView:(RMMapView *)mapView layerForAnnotation:(RMAnnotation *)annotation{
RMMarker *marker = [[RMMarker alloc] initWithMapboxMarkerImage:#"embassy"];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 50, 32)];
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.image = annotation.userInfo;
marker.leftCalloutAccessoryView = imageView;
marker.canShowCallout = YES;
return marker;
}
and this is how I get the teaser and build my annotation from the mbtiles file:
- (void)singleTapOnMap:(RMMapView *)mapView at:(CGPoint)point{
[mapView removeAllAnnotations];
RMMBTilesSource *source = (RMMBTilesSource *)mapView.tileSource;
if ([source conformsToProtocol:#protocol(RMInteractiveSource)] && [source supportsInteractivity])
{
NSString *formattedOutput = [source formattedOutputOfType:RMInteractiveSourceOutputTypeTeaser
forPoint:point
inMapView:mapView];
if (formattedOutput && [formattedOutput length])
{
// parse the country name out of the content
//
NSUInteger startOfCountryName = [formattedOutput rangeOfString:#"<strong>"].location + [#"<strong>" length];
NSUInteger endOfCountryName = [formattedOutput rangeOfString:#"</strong>"].location;
NSString *countryName = [formattedOutput substringWithRange:NSMakeRange(startOfCountryName, endOfCountryName - startOfCountryName)];
// parse the flag image out of the content
//
NSUInteger startOfFlagImage = [formattedOutput rangeOfString:#"base64,"].location + [#"base64," length];
NSUInteger endOfFlagImage = [formattedOutput rangeOfString:#"\" style"].location;
UIImage *flagImage = [UIImage imageWithData:[NSData dataFromBase64String:[formattedOutput substringWithRange:NSMakeRange(startOfFlagImage, endOfFlagImage)]]];
RMAnnotation *annotation = [RMAnnotation annotationWithMapView:mapView coordinate:[mapView pixelToCoordinate:point] andTitle:countryName];
annotation.userInfo = flagImage;
[mapView addAnnotation:annotation];
[mapView selectAnnotation:annotation animated:YES];
}
}
}
UPDATED
I figured out how to do that by using a leftCalloutAccessoryView on the marker (I added the following at the end of layerForAnnotation method :
marker.leftCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
and used the following delegate method to track the event:
-(void)tapOnCalloutAccessoryControl:(UIControl *)control forAnnotation:(RMAnnotation *)annotation onMap:(RMMapView *)map{
NSLog(#"I will now pass to the next screen ! YEAH! %#",annotation.title);
}
The problem now is that I want to get rid off the left calloutAccesoryView. Any suggestions?
None of what you have here about the map is needed. What you really want is the callout (which is of type SMCalloutView, a dependent project to the Mapbox SDK that you are using) to have its clicks registered.
Check out this issue for more details:
https://github.com/mapbox/mapbox-ios-sdk/issues/422

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 IGAMapAnnotations 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 NSStrings.
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;
}

MKPinAnnotationView color is not working

I am trying to show some pins from an array, it shows them all but they are red, and not green as i ask them to be.
Why is that ?
//run on array to get all locations
for(int k=0;k<[array count];k=k+2)
{
float targetlat=[[array objectAtIndex:k] floatValue];
float targetlongi=[[array objectAtIndex:k+1] floatValue];
CLLocationCoordinate2D location = CLLocationCoordinate2DMake(targetlat,targetlongi);
NSString *partyTitle = #"title";
MKPinAnnotationView *partyPin = [self returnPointView:location andTitle:partyTitle andColor:MKPinAnnotationColorGreen];
[self.mapView addAnnotation:partyPin.annotation];
}
//function callback is working but its red, and it takes so much time to load
-(MKPinAnnotationView*) returnPointView: (CLLocationCoordinate2D) location andTitle: (NSString*) title andColor: (int) color
{
MKCoordinateRegion region = self.mapView.region;
region.center = location;
region.span.longitudeDelta /= 5.0;
region.span.latitudeDelta /= 5.0;
[self.mapView setRegion:region];
MKPointAnnotation *resultPin = [[MKPointAnnotation alloc] init];
MKPinAnnotationView *result = [[MKPinAnnotationView alloc] initWithAnnotation:resultPin reuseIdentifier:Nil];
[resultPin setCoordinate:location];
resultPin.title = title;
result.pinColor = color;
return result;
}
Regarding the main issue that the pins are red instead of green:
The code creates an MKPinAnnotationView but this view is never given to the map view.
To make the map view use annotation views that you create, you must implement the viewForAnnotation delegate method and return them from there.
Otherwise, the map view has no knowledge of annotation views that you create.
If you don't implement viewForAnnotation, the map view creates a default red pin view.
Regarding the second issue that "it takes so much time to load":
The most likely reason for this is that you are calling setRegion each time you add an annotation.
If you are adding, say, 500 annotations, the map view is setting the region 500 times.
Please note that it is not necessary to call setRegion simply to add an annotation (regardless of the currently-visible region). The annotation's coordinate does not have to be "visible" to add an annotation there.
What you want to do inside the for loop is simply construct a region that includes all the annotations and then call setRegion (or setVisibleRect) once and after all the annotations are added (after the for loop). Constructing an MKMapRect and calling setVisibleMapRect is easier than constructing an MKCoordinateRegion in order to call setRegion.
In iOS 7, this is even simpler: Just call showAnnotations (no manual construction necessary).
Example:
//Initialize the MKMapRect (region) we want to show to null...
MKMapRect showMapRect = MKMapRectNull;
for(int k=0;k<[array count];k=k+2)
{
float targetlat=[[array objectAtIndex:k] floatValue];
float targetlongi=[[array objectAtIndex:k+1] floatValue];
CLLocationCoordinate2D location = CLLocationCoordinate2DMake(targetlat,targetlongi);
NSString *partyTitle = #"title";
//Here, don't create the annotation view.
//Just create the annotation...
MKPointAnnotation *resultPin = [[MKPointAnnotation alloc] init];
[resultPin setCoordinate:location];
resultPin.title = partyTitle;
[self.mapView addAnnotation:resultPin];
//Add this annotation's coordinate
//to the MKMapRect we want to show...
MKMapPoint annMapPoint = MKMapPointForCoordinate(location);
MKMapRect annMapRect = MKMapRectMake(annMapPoint.x, annMapPoint.y, 0, 0);
showMapRect = MKMapRectUnion(showMapRect, annMapRect);
}
mapView.visibleMapRect = showMapRect;
//In iOS 7, instead of constructing MKMapRect manually,
//we could just call showAnnotations...
//[mapView showAnnotations:mapView.annotations animated:YES];
//Implement the viewForAnnotation delegate method...
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
//if annotation is the user location,
//return nil so map view shows default view for it (blue dot)...
if ([annotation isKindOfClass:[MKUserLocation class]])
{
return nil;
}
static NSString *reuseId = #"pin";
MKPinAnnotationView *pav = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:reuseId];
if (pav == nil)
{
pav = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseId];
pav.canShowCallout = YES;
pav.animatesDrop = YES;
pav.pinColor = MKPinAnnotationColorGreen;
}
else
{
pav.annotation = annotation;
}
return pav;
}

mapview random annotation image

I'm having great trouble figuring out what I'm doing wrong here...
I am adding annotations to my mapview through loops, but the annotation image is completely random every time... when I NSLog the order, it's random - i'm not sure if that is the problem.
- (MKAnnotationView *)mapView:(MKMapView *)mapView1 viewForAnnotation:(id<MKAnnotation>)annotation {
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
if ([[annotation subtitle] isEqual:#"Bar"]) {
MKAnnotationView *view = nil;
view = [self.mapView dequeueReusableAnnotationViewWithIdentifier:#"myAnnotationIdentifier"];
if (!view) {
// Could not reuse a view ...
// Creating a new annotation view
view = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"myAnnotationIdentifier"];
view.enabled = YES;
view.canShowCallout = YES;
view.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
view.image = [UIImage imageNamed:#"beer.png"];
}
return view;
}
else if ([[annotation subtitle] isEqual:#"Club"]) {
MKAnnotationView *view = nil;
view = [self.mapView dequeueReusableAnnotationViewWithIdentifier:#"myAnnotationIdentifier"];
if (!view) {
// Could not reuse a view ...
// Creating a new annotation view
view = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"myAnnotationIdentifier"];
view.enabled = YES;
view.canShowCallout = YES;
view.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
view.image = [UIImage imageNamed:#"clubs.png"];
}
return view;
}
}
the view.image is totally random... either clubs.png or beer.png..
How do I make it correctly?
this is how I add the annotations:
- (void)barLoop {
for (int i = 0; i<barArray.count; i++) {
int index = [[barArray objectAtIndex:i]intValue];
NSString *lati = [[[self.usersLocationArray objectAtIndex:index]
valueForKeyPath:#"items.Latitude"]componentsJoinedByString:#""];
NSString *longi = [[[self.usersLocationArray objectAtIndex:index]
valueForKeyPath:#"items.Longitude"]componentsJoinedByString:#""];
NSString *barNavn = [[[self.usersLocationArray objectAtIndex:index] valueForKeyPath:#"items.Navn"]componentsJoinedByString:#""];
float latitude = [lati floatValue];
float longitude = [longi floatValue];
MKCoordinateRegion region = { {0.0, 0.0} , {0.0, 0.0} };
region.center.latitude = latitude;
region.center.longitude = longitude;
region.span.longitudeDelta = 0.20f;
region.span.latitudeDelta = 0.20f;
[mapView setRegion:region animated:NO];
CLLocationCoordinate2D location;
location.latitude = latitude;
location.longitude = longitude;
Annotation *ann = [[Annotation alloc]initWithPosition:location];
ann.title = barNavn;
ann.subtitle = #"Bar";
[self.mapView addAnnotation:ann];
}
}
thanks in advance :)
You have two types of annotations, but you are only setting the image when the annotation is originally created. Thus, if an annotation for a bar scrolls off the map view and another annotation for a club scrolls on, it might reuse the bar's annotation view for the club.
There are two ways of fixing this:
Use different reuseIdentifier parameters for each of the two types of annotation views; or
Set/reset the annotation view's image regardless of whether your call to dequeueReusableAnnotationViewWithIdentifier succeeded in returning a value or not.
Unrelated, but your viewForAnnotation method should:
You might want to use isEqualToString instead of isEqual when checking for #"Bar" vs #"Club".
Make sure to return nil if neither of those if clauses return true (this should never happen, but nonetheless you have a potential path in this routine in which you neglect to return any value). I would guess that this would have been brought to your attention if you ran the code through the static analyzer ("Analyze" on Xcode's "Product" menu).
A more subtle observation, but I'd probably not rely upon the annotation's subtitle for determining which annotation view to employ, but rather either
have two annotation subclasses, one for bars and one for clubs; or
have a custom property in your existing Annotation class that indicates whether it's a bar or club (and I'd probably use an enum for that).
At some future date, you might want to use the callout's subtitle for something other than "Bar" vs "Club" (e.g., maybe the address of the bar/club?), and the current model seems to conflate a UI attribute (i.e. what shows up in the callout) with a state property (i.e. a variable that controls which annotation view type to use). Not a big deal, but just an suggestion.

Resources