I've written some code for showing annotations with custom images in a mapview.
My mapview delegate implements this method to customize annotation when they are put in the map:
- (MKAnnotationView *) mapView:(MKMapView *) mapView viewForAnnotation:(id<MKAnnotation>) annotation {
if ([annotation isKindOfClass:[Station class]]) {
Station *current = (Station *)annotation;
MKPinAnnotationView *customPinview = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:nil];
if([[current type] compare:FONTANELLA]==NSOrderedSame)
customPinview.pinColor = MKPinAnnotationColorPurple;
else{
int test=current.bici;
if(test==0)
customPinview.image = [UIImage imageNamed:#"bicimir.png"];
else if(test<4)
customPinview.image = [UIImage imageNamed:#"bicimi.png"];
else if(test>=4)
customPinview.image = [UIImage imageNamed:#"bicimig.png"];
}
customPinview.animatesDrop = NO;
customPinview.canShowCallout = YES;
return customPinview;
}
else{
NSString *identifier=#"MyLocation";
MKPinAnnotationView *annotationView = (MKPinAnnotationView *) [_mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
return annotationView;
}
}
The problem is a strange behavior when I long click on a custom annotation on the map:
The image change and the default red pin is shown.
Why this behavior? And How can I avoid it?
When you want to use a custom image for an annotation view, create a generic MKAnnotationView instead of an MKPinAnnotationView.
The MKPinAnnotationView really likes to display its default image which is a pin.
Rearrange the logic a bit so that for FONTANELLA, it creates an MKPinAnnotationView but for the rest an MKAnnotationView.
Also, you should really implement annotation view re-use for all cases (and the last else part doesn't make sense since nothing is done if the dequeue doesn't return anything--you could just do return nil; instead).
inside .h file
#interface AddressAnnotation : NSObject<MKAnnotation> {
CLLocationCoordinate2D coordinate;
NSString *mPinColor;
}
#property (nonatomic, retain) NSString *mPinColor;
#end
in .m file
#implementation AddressAnnotation
#synthesize coordinate mPinColor;
- (NSString *)pincolor{
return mPinColor;
}
- (void) setpincolor:(NSString*) String1{
mPinColor = String1;
}
-(id)initWithCoordinate:(CLLocationCoordinate2D) c{
coordinate=c;
NSLog(#"%f,%f",c.latitude,c.longitude);
return self;
}
#end
inside .m class file
- (MKAnnotationView *) mapView:(MKMapView *)mapView1 viewForAnnotation:(AddressAnnotation *) annotation{
UIImage *anImage=[[UIImage alloc] init];
MKAnnotationView *annView=(MKAnnotationView*)[mapView1 dequeueReusableAnnotationViewWithIdentifier:#"annotation"];
if(annView==nil)
{
annView=[[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"annotation"] autorelease];
}
if([annotation.mPinColor isEqualToString:#"green"])
{
anImage=[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"Google pin green.png" ofType:nil]];
}
else if([annotation.mPinColor isEqualToString:#"red"])
{
anImage=[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"Google pin red.png" ofType:nil]];
}
else if([annotation.mPinColor isEqualToString:#"blue"])
{
anImage=[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"Google pin blue.png" ofType:nil]];
}
annView.image = anImage;
return annView;
}
Related
I am working on an old application and need to make changes in Mapview. Previously we need to show multiple annotations on mapview with same images on each pin but now we have to show different images on annotation view pins to display address. I am using the following code to display annotation pins but it always shows the same image on annotation pins.
Here is my code:
- (MKAnnotationView *) mapView:(MKMapView *)mapView1 viewForAnnotation:(id <MKAnnotation>) annotation
{
NSLog(#"Eventtype Array is %#",eventTypeArray);
MKAnnotationView * pinView = nil;
if(annotation != _mapvw.userLocation)
{
static NSString * defaultPinID = #"pinId";
pinView = (MKAnnotationView *)[_mapvw dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
if ( pinView == nil )
{
pinView = [[MKAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:defaultPinID];
}
for ( int i=0; i<[eventTypeArray count]; i++)
{
eventTypeStr = [NSString stringWithFormat:#"%#",
[eventTypeArray objectAtIndex:i]];
NSLog(#"Event Type is %#",eventTypeStr);
if ([eventTypeStr isEqualToString:#"0"])
{
NSLog(#"Eventtype Array is %#",eventTypeStr);
NSLog(#"Event Type is %#",eventTypeStr);
pinView.canShowCallout = YES;
pinView.image = [UIImage imageNamed:#"smiley.png"];
}
else if ([eventTypeStr isEqualToString:#"1"])
{
NSLog(#"Event Type is %#",eventTypeStr);
pinView.canShowCallout = YES;
pinView.image = [UIImage imageNamed:#"dollar1.png"];
}
else if ([eventTypeStr isEqualToString:#"2"])
{
NSLog(#"Event Type is %#",eventTypeStr);
pinView.canShowCallout = YES;
pinView.image = [UIImage imageNamed:#"donation.png"];
}
}
}
return pinView;
}
You’re iterating through your array of event types for every annotation, presumably always ending with the image associated for the last one in eventTypeArray.
Instead, you want the “event type” to be a property of the annotation. Then, when generating your annotation view, you can look at the annotation’s event type to know which image to use.
So, first, you haven’t done so already, you’d have an annotation that has an eventType property:
typedef NS_ENUM(NSUInteger, EventType) {
EventTypeSmiley,
EventTypeDollar,
EventTypeDonation,
};
#interface EventAnnotation: MKPointAnnotation
#property (nonatomic) EventType eventType;
#end
#implementation EventAnnotation
#end
Now, in this case, I’m using an enumeration for my event types, but you can use whatever type you want. (Even if you stick with the event type array, I’d still use an enumeration to excise cryptic 0/1/2 values sprinkled throughout your code.)
Then, when you add annotations to your map, use this new annotation type, not MKPointAnnotation:
EventAnnotation *eventAnnotation = [[EventAnnotation alloc] init];
eventAnnotation.coordinate = coordinate;
eventAnnotation.title = #"Fund raiser";
eventAnnotation.eventType = EventTypeDollar;
Now that all of your annotations are EventAnnotation, you can have your viewForAnnotation act accordingly:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
if ([annotation isKindOfClass:[MKUserLocation class]]) {
return nil;
}
NSAssert([annotation isKindOfClass:[EventAnnotation class]], #"Was expecting event annotation”); // obviously, handle non-EventAnnotation annotations however you want, but I’m going to catch failures for now
static NSString *identifier = #"EventAnnotation";
MKAnnotationView *annotationView = [_mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (!annotationView) {
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
annotationView.canShowCallout = YES;
} else {
annotationView.annotation = annotation; // don't forget this line!
}
EventAnnotation *eventAnnotation = (EventAnnotation *)annotation;
switch (eventAnnotation.eventType) {
case EventTypeSmiley:
annotationView.image = [UIImage imageNamed:#"smiley.png"];
break;
case EventTypeDollar:
annotationView.image = [UIImage imageNamed:#"dollar1.png"];
break;
case EventTypeDonation:
annotationView.image = [UIImage imageNamed:#"donation.png"];
break;
}
return annotationView;
}
I've got an MKMapView loaded with plenty of custom annotation (800 circa).
When I drag on the map and I return to an annotation, it's image has changed with another one. For me it's seems like a cache issue.
Pin before dragging
Pin after dragging
SuperClass header
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface MapAnnotation : NSObject <MKAnnotation>
#property (nonatomic, copy) NSString *title;
#property NSString * pinImageName;
#property (nonatomic) CLLocationCoordinate2D coordinate;
- (id)initWithCoordinate:(CLLocationCoordinate2D)coordinate title:(NSString *)title pinImageName:(NSString *)pinImageName;
- (MKAnnotationView *)getAnnotationView;
#end
SubClass (the one that creates the issue) header
#import "MapAnnotation.h"
#import "Company.h"
#interface CompanyAnnotation : MapAnnotation
#property Company *company;
#property UIImage *pinImage;
- (id)initWithCoordinate:(CLLocationCoordinate2D)coordinate title:(NSString *)title pinImage:(UIImage *)pinImage;
- (MKAnnotationView *)getAnnotationView;
#end
viewForAnnotation delegate method
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)annotation {
if (annotation == mapView.userLocation){
return nil;
}
MapAnnotation *location = [MapAnnotation new];
MKAnnotationView *annotationView = [MKAnnotationView new];
if ([annotation isKindOfClass:[CompanyAnnotation class]]) {
location = (CompanyAnnotation *)annotation;
annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:#"companyAnnotation"];
}
if (annotationView == nil) {
annotationView = [location getAnnotationView];
} else {
annotationView.annotation = annotation;
}
return annotationView;
}
getAnnotationView method
- (MKAnnotationView *)getAnnotationView {
MKAnnotationView * annotationView = [[MKAnnotationView alloc] initWithAnnotation:self reuseIdentifier:#"companyAnnotation"];
[annotationView setEnabled:YES];
[annotationView setCanShowCallout:YES];
[annotationView setContentMode:UIViewContentModeScaleAspectFit];
[annotationView setImage:self.pinImage];
[annotationView setFrame:CGRectMake(0, 0, 28, 44)];
[annotationView setRightCalloutAccessoryView:[UIButton buttonWithType:UIButtonTypeDetailDisclosure]];
return annotationView;
}
The dequeue is not being handled properly in viewForAnnotation because when dequeueReusableAnnotationViewWithIdentifier returns a previously-used view (when annotationView is not nil), the code is only updating that view's annotation property:
if (annotationView == nil) {
annotationView = [location getAnnotationView];
} else {
annotationView.annotation = annotation;
}
But the annotation view's image is not updated -- in a dequeued view, the image will be set to the one associated with the annotation the view was originally created for (when getAnnotationView was called).
So now the view appears at the new annotation's coordinates but the image is still from the previous annotation the view was used for.
There are various ways to fix this such as creating a proper subclass of MKAnnotationView that monitors changes to its annotation property and automatically updates all other properties associated with an annotation.
With the existing code, a simple way to fix it is to separate out the annotation-specific property changes into a separate method that can be called both when the view is created and when its annotation property is updated.
For example, in CompanyAnnotation, create a method like this:
-(void)configureView:(MKAnnotationView *)av
{
av.image = self.pinImage;
}
Then change getAnnotationView to:
- (MKAnnotationView *)getAnnotationView {
MKAnnotationView * annotationView = [[MKAnnotationView alloc] initWithAnnotation:self reuseIdentifier:#"companyAnnotation"];
//set properties here that are not specific to an annotation...
[annotationView setEnabled:YES];
[annotationView setCanShowCallout:YES];
[annotationView setContentMode:UIViewContentModeScaleAspectFit];
//[annotationView setImage:self.pinImage];
[annotationView setFrame:CGRectMake(0, 0, 28, 44)];
[annotationView setRightCalloutAccessoryView:[UIButton buttonWithType:UIButtonTypeDetailDisclosure]];
//set properties that are specific to an annotation...
[self configureView:annotationView];
return annotationView;
}
Finally in viewForAnnotation:
if (annotationView == nil) {
annotationView = [location getAnnotationView];
} else {
annotationView.annotation = annotation;
if ([annotation isKindOfClass:[CompanyAnnotation class]]) {
//update image, etc...
[annotation configureView:annotationView];
}
}
Note that MapAnnotation will have the same issue with the pinImageName property.
I'm trying to change my pin images in my map view. The annotations are created from parsing a KML file as below:
-(void)loadMap
{
NSURL *url = [NSURL fileURLWithPath:path];
kmlParser = [[KMLParser alloc] initWithURL:url];
[kmlParser parseKML];
NSArray *overlays = [kmlParser overlays];
[map addOverlays:overlays];
NSArray *annotations = [kmlParser points];
[map addAnnotations:annotations];
MKMapRect goTo = MKMapRectNull;
for (id <MKOverlay> overlay in overlays) {
if (MKMapRectIsNull(goTo)) {
goTo = [overlay boundingMapRect];
} else {
goTo = MKMapRectUnion(goTo, [overlay boundingMapRect]);
}
}
for (id <MKAnnotation> annotation in annotations) {
MKMapPoint annotationPoint = MKMapPointForCoordinate(annotation.coordinate);
MKMapRect pointRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0, 0);
if (MKMapRectIsNull(goTo)) {
goTo = pointRect;
} else {
goTo = MKMapRectUnion(goTo, pointRect);
}
}
map.visibleMapRect = goTo;
}
The code then runs the viewForAnnotation method below which should change my pin image to my custom pin image.
-(MKAnnotationView *)mapView:(MKMapView *)mV viewForAnnotation:(id <MKAnnotation>)annotation
{
MKAnnotationView *pinView = [kmlParser viewForAnnotation:annotation];
if(annotation != map.userLocation)
{
static NSString* AnnotationIdentifier = #"AnnotationIdentifier";
MKPinAnnotationView* pinView = [[MKPinAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:AnnotationIdentifier];
pinView.canShowCallout = YES;
pinView.animatesDrop = YES;
pinView.image = [UIImage imageNamed:#"bigPin.png"];
}
return pinView;
}
The viewForAnnotation method runs and it appears to change the image, but the map still shows the original pin image. What am I doing wrong?
The answer provided by Anna above solved the problem
See stackoverflow.com/questions/9814988/…. Code is creating an MKPinAnnotationView instead of plain MKAnnotationView.
The MKPinAnnotationView class provides a concrete annotation view that displays a pin icon like the ones found in the Maps application. Using this class, you can configure the type of pin to drop and whether you want the pin to be animated into place.This Class is inherited from MKAnnotationView but dont have image property itself.You can use the MKAnnotationView class as is or subclass it to provide custom behavior as needed. The image property of the class lets you set the appearance of the annotation view without subclassing directly.
So instead of Using MKPinAnnotationView use MKAnnotationView.
Check out this code for custom image rather than the default image of ios for pin annotations.
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
if (annotation == mapView.userLocation)
{
return nil;
}
else
{
MKAnnotationView *pinView = (MKAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:#"annot"];
if (!pinView)
{
// if an existing pin view was not available, create one
MKAnnotationView *customPinView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"annot"];
customPinView.image = [UIImage imageNamed:#"yourImage.png"];
return customPinView;
}
else
{
pinView.annotation = annotation;
}
return pinView;
}
}
I can't figure out how to set a disclosure button on 1 group of annotations.
The annotations are separated in 3 kinds of Arrays.
NSMutableArray *category1 = [[NSMutableArray alloc]init];
NSMutableArray *category2 = [[NSMutableArray alloc]init];
NSMutableArray *category3 = [[NSMutableArray alloc]init];
I add the annotation objects in these arrays like this:
myAnn = [[Annotations alloc]init];
location.latitude = 52.381285;
location.longitude = 4.888740;
myAnn.coordinate = location;
myAnn.title = #"";
myAnn.subtitle = #"";
[category1 addObject:myAnn];
And put them in another array:
[self.locationArrays addObject:category1];
I use a segmented control to show the 3 groups on the map.
currentAnnotation is an integer to use the objectAtIndex.
-(IBAction)setMap:(id)sender
{
int newAnnotations = ((UISegmentedControl *) sender).selectedSegmentIndex;
if (newAnnotations != self.currentAnnotation)
{
[self.myMapView removeAnnotations:[self.locationArrays objectAtIndex:self.currentAnnotation]];
[self.myMapView addAnnotations:[self.locationArrays objectAtIndex:newAnnotations]];
self.currentAnnotation = newAnnotations;
[self.myMapView showAnnotations:[self.locationArrays objectAtIndex:newAnnotations] animated:YES];
}
}
My question is:
How do the objects, for example, in catergory1 get a disclosure button?
And how should I use it with the viewForAnnotation and calloutAccessoryControlTapped method, because I already included all the annotation with a left blue car accessory.
Update
ViewForAnnotation method:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:#"MapVC"];
if (!annotationView)
{
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"MapVC"];
annotationView.canShowCallout = YES;
if ([annotation isKindOfClass:[MKUserLocation class]] || annotation == myMapView.userLocation || annotation == locationManager)
{
return nil;
}
else
{
//Blauw Navigatie Auto...
UIImageView *carView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Driving"]];
UIButton *blueView = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 44, 44+30)];
blueView.backgroundColor = [UIColor colorWithRed:0 green:0.5 blue:1 alpha:1];
carView.frame = CGRectMake(11, 14, carView.image.size.width, carView.image.size.height);
[blueView addSubview:carView];
annotationView.leftCalloutAccessoryView = blueView;
}
if ([annotation isKindOfClass:[Annotations class]])
{
Annotations *myRightAnn = (Annotations *)annotation;
if (myRightAnn.category == 1)
{
annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
}
else
{
annotationView.rightCalloutAccessoryView = nil;
}
}
}
return annotationView;
}
In your Annotations class (the one that seems to implement MKAnnotation), add a property named "category" (the name has nothing to do with "Objective-C Categories" -- it's just in reference to what you've called your three arrays: category1..3, use a different name if you like):
#interface Annotations : NSObject <MKAnnotation>
#property (nonatomic, assign) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *subtitle;
#property (nonatomic, assign) int category; //<-- add this property
#end
Set the annotation's category property to 1, 2, 3, or whatever when creating the annotation:
myAnn = [[Annotations alloc]init];
location.latitude = 52.381285;
location.longitude = 4.888740;
myAnn.coordinate = location;
myAnn.title = #"";
myAnn.subtitle = #"";
myAnn.category = 1; //<-- set to 1 because adding to category1 array
[category1 addObject:myAnn];
Then in viewForAnnotation, check the annotation's category property and set the rightCalloutAccessoryView as needed (below is just a rough idea of the code):
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
if ([annotation isKindOfClass:[MKUserLocation class]]) {
return;
}
static NSString *reuseId = #"ann";
MKAnnotationView *av = [mapView dequeueReusable...
if (av == nil) {
av = [[MKAnnotationView alloc] initWithAnnotation...
av.image = ...
av.leftCalloutAccessoryView = ...
av.canShowCallout = YES;
//Set other properties here that will be
//the same for ALL annotations...
}
else {
av.annotation = annotation;
}
//Set rightCalloutAccessoryView based on annotation **AFTER**
//the dequeue/alloc+init...
if ([annotation isKindOfClass:[Annotations class]]) {
Annotations *myAnn = (Annotations *)annotation;
if (myAnn.category == 1) {
//set right view to button only for category 1...
av.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
}
else {
//no right view for any other categories...
av.rightCalloutAccessoryView = nil;
}
}
return av;
}
In calloutAccessoryControlTapped, check the annotation's class, cast it to Annotations and check the category if necessary and handle it as needed:
-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
if ([view.annotation isKindOfClass:[Annotations class]]) {
Annotations *myAnn = (Annotations *)view.annotation;
if (control == view.leftCalloutAccessoryView) {
//handle left control tap...
}
else
if (control == view.rightCalloutAccessoryView) {
//handle right control tap...
}
}
}
Everything is working fine in my app except for one thing: after zooming in and zooming back out, to see the whole map, some callouts open the wrong detailview.
I don't know if I'm missing some code or else.
Using Xcode 5.1.1 for iOS7.
This is what I've got at the moment:
Annotation.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface Annotation: NSObject <MKAnnotation>
#property (nonatomic, assign) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *subtitle;
#end
Annotation.m
#import "Annotation.h"
#implementation Annotation
#synthesize coordinate,title,subtitle;
#end
MapView.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#interface Nameofthemap : UIViewController <MKMapViewDelegate>
#property (strong, nonatomic) IBOutlet MKMapView *Nameofthemap;
#end
MapView.m
#import "MapView.h"
#import "Annotation.h"
#import "InfoViewController.h"
#import "InfoTwoViewController.h"
#interface MapView ()
#property (nonatomic, strong) IBOutlet InfoViewController *InfoViewController;
#property (nonatomic, strong) IBOutlet InfoTwoViewController *InfoTwoViewController;
#end
#define PLACE1_LATITUDE 43.777130;
#define PLACE2_LONGITUDE 10.790018;
#define PLACE2_LATITUDE 43.81471237;
#define PLACE2_LONGITUDE 10.67472765;
#implementation MapView
- (IBAction)changeMapType:(id)sender {
if (_MapView.mapType == MKMapTypeHybrid)
_MapView.mapType = MKMapTypeStandard;
else
_MapView.mapType = MKMapTypeHybrid;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self gotoLocation];
_MapView.showsUserLocation = YES;
}
- (void)gotoLocation
{
MKCoordinateRegion newRegion;
newRegion.center.latitude = PLACE1_LATITUDE;
newRegion.center.longitude = PLACE2_LONGITUDE;
newRegion.span.latitudeDelta = 0.25f;
newRegion.span.longitudeDelta = 0.25f;
[self.MapView setRegion:newRegion animated:YES];
NSMutableArray * locations = [[NSMutableArray alloc] init];
CLLocationCoordinate2D location;
Annotation *myAnn;
Annotation *myAnn2;
//Place1 annotation
myAnn = [[Annotation alloc] init];
location.latitude = PLACE1_LATITUDE;
location.longitude = PLACE1_LONGITUDE;
myAnn.coordinate = location;
myAnn.title = #"Name of the place";
myAnn.subtitle = #"Details";
[locations addObject:myAnn];
//Place2 annotation
myAnn2 = [[Annotation alloc] init];
location.latitude = PLACE2_LATITUDE;
location.longitude = PLACE2_LONGITUDE;
myAnn2.coordinate = location;
myAnn2.title = #"Name of place two";
myAnn2.subtitle = #"Details";
[locations addObject:myAnn2];
[self->_MapView addAnnotations:locations];
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control{
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)myAnn {
if ([myAnn isKindOfClass:[MKUserLocation class]])
{
((MKUserLocation *)myAnn).title = #"Your position";
return nil;
}
MKPinAnnotationView *pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:#"pinView"];
if (!pinView) {
pinView = [[MKPinAnnotationView alloc] initWithAnnotation:myAnn reuseIdentifier:#"pinView"];
pinView.pinColor = MKPinAnnotationColorRed;
pinView.canShowCallout = YES;
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
if ([[myAnn title] isEqualToString:#"Name of the place"]){
[rightButton addTarget:self action:#selector(myAnnClicked:)forControlEvents:UIControlEventTouchUpInside];
}
if ([[myAnn title] isEqualToString:#"Name of place two"]){
[rightButton addTarget:self action:#selector(myAnn2Clicked:)forControlEvents:UIControlEventTouchUpInside];
}
pinView.rightCalloutAccessoryView = rightButton;
}
return pinView;
}
-(IBAction)myAnnClicked:(id)sender
{
InfoViewController *info = [[InfoViewController alloc]init];
[self.navigationController pushViewController:info animated:YES];
}
-(IBAction)myAnn2Clicked:(id)sender
{
InfoTwoController *info2 = [[InfoTwoController alloc]init];
[self.navigationController pushViewController:info2 animated:YES];
}
#end
It's an annotation view re-use issue.
In viewForAnnotation, the button targets are only being set when creating a view (if dequeueReusableAnnotationViewWithIdentifier returns nil).
But if dequeueReusableAnnotationViewWithIdentifier returns a previously-used view, the button target is still whatever was set for the annotation that used the view before.
That previous annotation may not be the same as the current annotation.
So it's possible for annotation "two" to re-use a view that was originally created for annotation "one" and tapping on the already-created button shows the info for "one" instead of "two".
To fix this, two things should be done:
If dequeueReusableAnnotationViewWithIdentifier returns a view (if pinView is not nil), the code must update the view's annotation property to the current annotation.
The button target must be set whether a new view is being created or a dequeued view is being re-used. The easiest way to do this is to move the button creation/setting after the main if and just before the return.
The updated viewForAnnotation would look like this:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)myAnn {
if ([myAnn isKindOfClass:[MKUserLocation class]])
{
((MKUserLocation *)myAnn).title = #"Your position";
return nil;
}
MKPinAnnotationView *pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:#"pinView"];
if (!pinView) {
pinView = [[MKPinAnnotationView alloc] initWithAnnotation:myAnn reuseIdentifier:#"pinView"];
pinView.pinColor = MKPinAnnotationColorRed;
pinView.canShowCallout = YES;
}
else
{
//1. Re-using a view, update which annotation it's being used for now
pinView.annotation = myAnn;
}
//2. Now pinView is either a new view or re-used view.
//Set its button target based on current annotation...
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
if ([[myAnn title] isEqualToString:#"Name of the place"]){
[rightButton addTarget:self action:#selector(myAnnClicked:)forControlEvents:UIControlEventTouchUpInside];
}
if ([[myAnn title] isEqualToString:#"Name of place two"]){
[rightButton addTarget:self action:#selector(myAnn2Clicked:)forControlEvents:UIControlEventTouchUpInside];
}
pinView.rightCalloutAccessoryView = rightButton;
return pinView;
}
By the way, instead of creating separate methods for each annotation (which can get tedious), use the map view's calloutAccessoryControlTapped delegate method instead.
In fact, right now, the map view is calling both your custom methods and the calloutAccessoryControlTapped delegate method (in which there's no code currently).
In the delegate method, the annotation tapped is accessible via view.annotation.
So in viewForAnnotation, you would just do this:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)myAnn {
if ([myAnn isKindOfClass:[MKUserLocation class]])
{
((MKUserLocation *)myAnn).title = #"Your position";
return nil;
}
MKPinAnnotationView *pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:#"pinView"];
if (!pinView) {
pinView = [[MKPinAnnotationView alloc] initWithAnnotation:myAnn reuseIdentifier:#"pinView"];
pinView.pinColor = MKPinAnnotationColorRed;
pinView.canShowCallout = YES;
pinView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
}
else
{
pinView.annotation = myAnn;
}
return pinView;
}
Then in the calloutAccessoryControlTapped delegate method, you can do something like this:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control{
if ([view.annotation isKindOfClass:[Annotation class]])
{
Annotation *myAnn = (Annotation *)view.annotation;
id vcToPush = nil;
if ([[myAnn title] isEqualToString:#"Name of the place"]) {
vcToPush = [[InfoViewController alloc]init];
}
else if ([[myAnn title] isEqualToString:#"Name of place two"]) {
vcToPush = [[InfoTwoController alloc]init];
}
[self.navigationController pushViewController:vcToPush animated:YES];
}
}
Then remove the myAnnClicked and myAnn2Clicked methods.
You would also be much better off creating a generic "Info" view controller instead of a separate one for each annotation.
Some other unrelated things:
Don't put a semi-colon at the end of the #define lines
You've defined PLACE2_LONGITUDE twice
newRegion.center is using PLACE2_LONGITUDE instead of PLACE1_LONGITUDE