I have a MKMapView with a lot of annotation pins defined from a parser xml; thats my code:
-(IBAction)LoadAnnotation:(id)sender {
RXML element ...
RXML iterate....
[myMap removeAnnotations:myMap.annotations];
annotation.title = // NSString from RXML parser
annotation.subtitle = // NSString from RXML parser
myValue = // float value from RXML parser
[mymap addAnnotation:annotation];
}
and then
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation2 {
MKPinAnnotationView *pinView=[[MKPinAnnotationView alloc] initWithAnnotation:annotation2 reuseIdentifier:#"MyPin"];
if ( myValue > 0 && myValue < 10) {
pinView.canShowCallout = YES;
pinView.pinColor = MKPinAnnotationColorRed;
pinView.animatesDrop=YES;
return pinView;
}
else if ( myValue > 10 && myValue < 20 ) {
pinView.canShowCallout = YES;
pinView.pinColor = MKPinAnnotationColorGreen;
pinView.animatesDrop=YES;
return pinView;
}
pinView.canShowCallout = YES;
pinView.pinColor = MKPinAnnotationColorPurple;
pinView.animatesDrop=YES;
return pinView;
}
All right, when my MKMapView is loaded, I can see title annotations, subtitle annotations and all the pins with different colours.
But if I scroll and zoom IN the map at a certain level, then zoom OUT again, all the pins become PURPLE. What's happening there?
I have tried also using same "annotation" (id) in the two methods (and not "annotation" and "annotation2"), but I have no result.
Is there a way to avoid that and keep pinColors after map scroll and zoom?
The viewForAnnotation delegate method isn't necessarily called only once for each annotation nor is it guaranteed to be called in the order that you add the annotations. It's also called for the user location (blue dot) if you set showsUserLocation to YES.
When you zoom or pan the map, the map view will call the delegate method (again) as annotations come back into view. At that time, your myValue will have no relevance to the annotation the map is requesting a view for.
Instead of using a class-level ivar, add myValue as a property of your annotation class and set it along with the title and subtitle before you call addAnnotation:
annotation.title = // NSString from RXML parser
annotation.subtitle = // NSString from RXML parser
annotation.myValue = // float value from RXML parser
^^^^^^^^^^^
Then in viewForAnnotation, use the myValue property from the annotation parameter instead of an ivar. This way, the delegate method is always using information specific to the annotation it is requesting a view for:
if ( ! [annotation isKindOfClass:[MyCustomAnnotationClass class]])
{
return nil; //return a default view if not your custom class
}
//cast annotation to custom class so we can get the custom property...
MyCustomAnnotationClass *myPin = (MyCustomAnnotationClass *)annotation;
//use the custom property in the annotation instead of ivar...
if (myPin.myValue > 0 && myPin.myValue < 10) {
....
Changing the name of the annotation parameter to annotation2 is not necessary.
Unrelated but you should be implementing annotation view re-use by using dequeueReusableAnnotationViewWithIdentifier (search for it in the SDK or on SO). It can help with performance if there are a lot of annotations.
Your MyAnnotation class should look like this:
#interface MyAnnotation : NSObject <MKAnnotation> {
float myValue;
}
#property (nonatomic, assign) CLLocationCoordinate2D coordinate; //<-- add
#property (nonatomic, copy) NSString *title; //<-- add
#property (nonatomic, copy) NSString *subtitle; //<-- add
#property (nonatomic, assign) float myValue;
#end
#implementation MyAnnotation
#synthesize coordinate; //<-- add
#synthesize title; //<-- add
#synthesize subtitle; //<-- add
#synthesize myValue;
#end
The place where you add the annotation should look like this:
MyAnnotation *annotation = [[MyAnnotation alloc] init];
annotation.title = someTitle; // NSString from RXML parser
annotation.subtitle = someSubTitle; // NSString from RXML parser
annotation.myValue = someValue; // someValue from RXML parser
[mapView addAnnotation:annotation];
Above, someValue is whatever myValue was in your original ViewController.
Also, in viewForAnnotation, change annotation2 back to annotation and don't forget to put MKPinAnnotationView *pinView =[[MKPinAnnotationView alloc] init... before the MyAnnotation *myPin = ... line.
Related
I am trying to add annotations to a MKMapView
I have created a class CustomMapPin which conforms to the MKAnnotation protocol
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface CustomMapPin : NSObject <MKAnnotation> {
CLLocationCoordinate2D coordinate;
NSString *title;
NSString *subtitle;
}
#property(nonatomic, assign) CLLocationCoordinate2D coordinate;
#property(nonatomic, copy) NSString *title;
#property(nonatomic, copy) NSString *subtitle;
#property(nonatomic, strong) NSString *type; // this is to differentiate between the different annotations on the map
#end
I have created a class CustomMapAnnotationView which is a subclass of MKAnnotationView
CustomMapAnnotationView.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface CustomMapAnnotationView : MKAnnotationView
#property (nonatomic, strong) UIImageView *annotationImage;
#end
CustomMapAnnotationView.m
#import "CustomMapAnnotationView.h"
#implementation CustomMapAnnotationView
-(id) initWithAnnotation:(id<MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
if (self) {
self.frame = CGRectMake(0, 0, 28, 40);
self.backgroundColor = [UIColor clearColor];
self.annotationImage = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 28, 40)];
self.annotationImage.contentMode = UIViewContentModeScaleAspectFit;
[self addSubview:self.annotationImage];
}
return self;
}
#end
I am adding the custom pins inside FindMechanicViewController which is a CLLocationManagerDelegate and MKMapViewDelegate
The code snippet is:
-(void) viewWillAppear:(BOOL)animated {
[self.locationManager startUpdatingLocation];
self.currentLocation = [self.locationManager location];
// Set the region of the map
MKCoordinateSpan mapViewSpan = MKCoordinateSpanMake(0.01, 0.01);
MKCoordinateRegion mapRegion = MKCoordinateRegionMake(self.currentLocation.coordinate, mapViewSpan);
[self.mapView setRegion:mapRegion];
// Add custom pin showing user location
CustomMapPin *annotation = [[CustomMapPin alloc] init];
annotation.title = #"Current Location";
annotation.coordinate = self.currentLocation.coordinate;
annotation.type = #"user location";
[self.mapView addAnnotation:annotation];
}
And the delegate method
-(MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
static NSString *reuseId = #"CustomMapPin";
CustomMapAnnotationView *annotationView = (CustomMapAnnotationView *) [self.mapView dequeueReusableAnnotationViewWithIdentifier:reuseId];
if ([annotation isKindOfClass:[CustomMapPin class]]) {
CustomMapPin *customAnnotation = (CustomMapPin *)annotation;
if ([customAnnotation.type isEqualToString:#"user location"]) {
[annotationView setAnnotation:customAnnotation];
[annotationView setImage:[UIImage imageNamed:#"pin_user"]];
[annotationView setCanShowCallout:YES];
}
}
return annotationView;
}
This does not show anything on the map. How do I fix this ?
In viewForAnnotation, CustomMapAnnotationView is never actually alloc+inited (the dequeueReusableAnnotationViewWithIdentifier does not do this).
If viewForAnnotation is even getting called, it must be returning nil which means the map view must be putting a red pin somewhere (and if the delegate method isn't getting called, then again the map view will default to a red pin). Log the coordinates where the annotation is being added and look there.
A corrected viewForAnnotation might look like this:
-(MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
if ([annotation isKindOfClass:[CustomMapPin class]]) {
static NSString *reuseId = #"CustomMapPin";
CustomMapAnnotationView *annotationView = (CustomMapAnnotationView *) [self.mapView dequeueReusableAnnotationViewWithIdentifier:reuseId];
if (!annotationView) {
annotationView = [[CustomMapAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseId];
[annotationView setCanShowCallout:YES];
}
CustomMapPin *customAnnotation = (CustomMapPin *)annotation;
//view's annotation should be set regardless of "type"
[annotationView setAnnotation:customAnnotation];
if ([customAnnotation.type isEqualToString:#"user location"]) {
[annotationView setImage:[UIImage imageNamed:#"pin_user"]];
}
else {
//If it's not a "user location", then set image
//to something else otherwise image will either be nil
//or it will show some other annotation's image...
[annotationView setImage:[UIImage imageNamed:#"pin_other"]];
}
return annotationView;
}
return nil;
}
Some other issues with the code:
In viewWillAppear, code is retrieving the location immediately after calling startUpdatingLocation. This is not guaranteed to work every time and even if it does "work", you will most likely be getting an old, cached location. When it doesn't work, the annotation will either end up at 0,0 (Atlantic Ocean) or app will crash due to invalid coordinates. It's much better to read the location in the didUpdateLocations delegate method. In viewWillAppear, call startUpdatingLocation and then in didUpdateLocations, if the accuracy and age of the location is adequate for you, call stopUpdatingLocation and then use the location (create and add the annotation, etc).
In CustomMapAnnotationView, annotationImage object is created and added but its image property is never set. The annotationImage is actually never really used for anything.
The whole CustomMapAnnotationView class is unnecessary for your purpose . In viewForAnnotation, code is setting the image property on the CustomMapAnnotationView (instead of using annotationImage). This image property is inherited from MKAnnotationView. The built-in, basic MKAnnotationView class is all you need for what you're doing. It already has an image property and it automatically displays it for you (you don't need to create your own UIImageView).
I have an array of images, that are associated with each Annotation on my map. I can statically add an image to the leftCalloutAccessoryView but I am unsure how to make this dynamic. I hope its clear what I am asking. Each annotation has its own individual image that I want to display but I am unsure of how to reference the image in the following method;
- (MKAnnotationView *)mapView:(MKMapView *)mv viewForAnnotation:(id <MKAnnotation>)annotation
{
if([annotation isKindOfClass:[MKUserLocation class]])
return nil;
NSString *annotationIdentifier = #"PinViewAnnotation";
MyAnnotationView *pinView = (MyAnnotationView *) [mv dequeueReusableAnnotationViewWithIdentifier:annotationIdentifier];
if (!pinView)
{
pinView = [[MyAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:annotationIdentifier];
pinView.canShowCallout = YES;
UIImageView *houseIconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Icon"]];//static image
[houseIconView setFrame:CGRectMake(0, 0, 30, 30)];
pinView.leftCalloutAccessoryView = houseIconView;
}
else
{
pinView.annotation = annotation;
}
return pinView;
}
My array "self.sandwiches" contains Sandwich objects that have a name (NSString) and an imageName ('NSString').
Im looking for a solution where I can get the index of the pin that is selected, similar to a UITableView where you can get its index, and access it from the array using indexPath.row.
My Annotation class;
.h
#import
#import
#import
#interface SandwichAnnotation : NSObject<MKAnnotation>
#property (nonatomic,assign) CLLocationCoordinate2D coordinate;
#property (nonatomic,copy) NSString * title;
#property (nonatomic,copy) NSString * subtitle;
#end
.m
#import "SandwichAnnotation.h"
#implementation SandwichAnnotation
#synthesize coordinate,title,subtitle;
#end
In viewForAnnotation, rather than "getting the index of the pin" (which would work but is less efficient here than with a UITableView), I suggest adding the data required to the annotation class itself.
This way, the data is more self-contained and the code in the delegate method or elsewhere doesn't need to worry, know, or be kept in sync with where or what kind of structure the annotation object is stored in. As long as you have a reference to the annotation object, you will immediately have all the data needed for that annotation (or at least it will contain references to the related data within itself).
The viewForAnnotation delegate method provides a reference to the annotation object it needs a view for (the annotation parameter). It's typed generically as id<MKAnnotation> but it is actually an instance of the exact type that was created (either SandwichAnnotation by you or MKUserLocation by the map view).
One option is to make the parent Sandwich class itself implement MKAnnotation and eliminate the SandwichAnnotation class. This way, no searching or references are needed at all since the annotation parameter will actually be a Sandwich.
However, you may want to keep a separate class for your annotation objects (which is fine). In this case, you can add a reference to the parent object(s) in the annotation class. Example:
#interface SandwichAnnotation : NSObject<MKAnnotation>
#property (nonatomic,assign) CLLocationCoordinate2D coordinate;
#property (nonatomic,copy) NSString * title;
#property (nonatomic,copy) NSString * subtitle;
#property (nonatomic,retain) Sandwich * whichSandwich; // <-- add reference
#end
When creating a SandwichAnnotation, set the reference to which Sandwich the annotation is for:
for (Sandwich *currentSandwich in self.sandwiches) {
SandwichAnnotation *sa = [[SandwichAnnotation alloc] init...];
sa.coordinate = ...
sa.title = ...
sa.whichSandwich = currentSandwich; // <-- set reference
[mapView addAnnotation:sa];
}
Finally, in viewForAnnotation, if annotation is of type SandwichAnnotation, set the leftCalloutAccessoryView:
- (MKAnnotationView *)mapView:(MKMapView *)mv viewForAnnotation:(id <MKAnnotation>)annotation
{
if (! [annotation isKindOfClass:[SandwichAnnotation class]]) {
//If annotation is not a SandwichAnnotation, return default view...
//This includes MKUserLocation.
return nil;
}
//At this point, we know annotation is of type SandwichAnnotation.
//Cast it to that type so we can get at the custom properties.
SandwichAnnotation *sa = (SandwichAnnotation *)annotation;
NSString *annotationIdentifier = #"PinViewAnnotation";
MyAnnotationView *pinView = (MyAnnotationView *) [mv dequeueReusableAnnotationViewWithIdentifier:annotationIdentifier];
if (!pinView)
{
pinView = [[MyAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:annotationIdentifier];
pinView.canShowCallout = YES;
//Here, just initialize a blank UIImageView ready to use.
//Set image below AFTER we have a dequeued or new view ready.
UIImageView *houseIconView = [[UIImageView alloc] init];
[houseIconView setFrame:CGRectMake(0, 0, 30, 30)];
pinView.leftCalloutAccessoryView = houseIconView;
}
else
{
pinView.annotation = annotation;
}
//At this point, we have a dequeued or new view ready to use
//and pointing to the correct annotation.
//Update image on the leftCalloutAccessoryView here
//(not just when creating the view otherwise an annotation
//that gets a dequeued view will show an image of another annotation).
UIImageView *houseIconView = (UIImageView *)pinView.leftCalloutAccessoryView;
NSString *saImageName = sa.whichSandwich.imageName;
UIImage *houseIcon = [UIImage imageNamed: saImageName];
if (houseIcon == nil) {
//In case the image was not found,
//set houseIcon to some default image.
houseIcon = someDefaultImage;
}
houseIconView.image = houseIcon;
return pinView;
}
In my app, I have a mapView and few other components.
I have changed the view of annotation into my own icons, the icons gets displayed in the specific location of the the latitude and longitude.
But, when I click and hold the icons, it automatically gets converted into the anotations.
Please help me.
This is my map while loading
It becomes as such When I click and hold the annotations
didUpdateUserLocation
{
CLLocationCoordinate2D first;
first.latitude=13.040202;
first.longitude=80.24298;
myAnnotation.coordinate=first;
[locations addObject:myAnnotation];
[self.mapView addAnnotations:locations];
}
viewForAnnotation:
{
static NSString *identifier = #"Wifintech";
pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if ( pinView == nil )
pinView = [[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:identifier];
pinView.image = [UIImage imageNamed:#"car-side.png"];
pinView.canShowCallout = YES;
}
didSelectAnnotationView
{
float latitude = [[view annotation ] coordinate].latitude;
float longitude = [[view annotation ] coordinate].longitude;
title_value=[[view annotation] title];
NSString * subtitle_val =[[view annotation] subtitle];
title_para.text=[NSString stringWithFormat:#"%#, %#",title_value,subtitle_val];
latitude_value.text=[NSString stringWithFormat:#"%f",latitude];
longitude_value.text=[NSString stringWithFormat:#"%f",longitude];
}
When using your own images, you need to create an MKAnnotationView not an MKPinAnnotationView. Please try searching on SO for answers before posting questions (it will save everyone including you a lot of time) .
MKAnnotationView ClassReference
Try returning a MKAnnotationView instead of MKPinAnnotationView from
-mapView:viewForAnnotation:
Most likely the subclass overrides some method that are tracking the selected-state of the view and adjusting the image. I suspect this will not be the case when using MKAnnotationView.
I think you need to take a close look at the following documentation.
Also, maybe make sure that the view in didSelect.. method is actually a pinAnnotionView and that the image is what you think it is.
From the documentation on MKAnnotationView:
Annotation views support the concept of a selection state, which determines whether the view is unselected, selected, or selected and displaying a standard callout view. The user toggles between the selection states through interactions with the annotation view. In the unselected state, the annotation view is displayed but not highlighted. In the selected state, the annotation is highlighted but the callout is not displayed. And finally, the annotation can be displayed both with a highlight and a callout. The callout view displays additional information such as a title string and controls for viewing more information. The title information is provided by the annotation object but your annotation view is responsible for providing any custom controls. For more information, see the subclassing notes.
This is my code
#import "FirstViewController.h"
#import "LocationObject.h"
#define THE_SPAN 0.01f;
#interface FirstViewController ()
#property (weak, nonatomic) IBOutlet MKMapView *mapView;
#property (weak, nonatomic) IBOutlet UITextField *latitude_value;
#property (weak, nonatomic) IBOutlet UITextField *longitude_value;
#property (copy, nonatomic) NSString *title_value;
#property(nonatomic,assign) CLLocationCoordinate2D myCoordinate;
#property(nonatomic,copy) NSMutableArray *locations;
#property(nonatomic,retain) MKPointAnnotation * myAnnotation;
#property (readwrite) int tag;
#property(retain) MKPinAnnotationView *pinView;
#property (weak, nonatomic) IBOutlet UITextView *title_para;
#property(assign) CLLocationCoordinate2D *coordsArray;
#property(nonatomic,assign) MKPolyline * routeLine;
#end
#implementation FirstViewController
#synthesize latitude_value;
#synthesize longitude_value;
#synthesize title_value;
#synthesize myCoordinate;
#synthesize mapView;
#synthesize locations;
#synthesize myAnnotation;
#synthesize tag;
#synthesize pinView;
#synthesize title_para;
#synthesize title;
#synthesize coordsArray;
#synthesize routeLine;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
[self setMapView:nil];
[self setLatitude_value:nil];
[self setLongitude_value:nil];
[self setLatitude_value:nil];
[self setLongitude_value:nil];
[self setTitle_value:nil];
[self setTitle_para:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay {
MKPolylineView *polyLineView = [[MKPolylineView alloc] initWithPolyline:routeLine];
polyLineView.fillColor = [UIColor blueColor];
polyLineView.strokeColor = [UIColor redColor];
polyLineView.lineWidth = 2;
return polyLineView;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
//map view : region nad annotation
#pragma mark map view delegate methods
-(void) mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
//map region and center location
MKCoordinateRegion myRegion;
CLLocationCoordinate2D center;
center.latitude=13.040223;
center.longitude=80.240995;
MKCoordinateSpan span;
span.latitudeDelta=THE_SPAN;
span.longitudeDelta=THE_SPAN;
myRegion=MKCoordinateRegionMake(center, span);
MKCoordinateRegion adjusted_region=[self.mapView regionThatFits:myRegion];
[self.mapView setRegion:adjusted_region animated:YES];
locations=[[NSMutableArray alloc]init];
//first point
CLLocationCoordinate2D first;
first.latitude=13.040202;
first.longitude=80.24298;
myAnnotation = [[MKPointAnnotation alloc]init];
myAnnotation.coordinate=first;
myAnnotation.title=#"Wifin Technology";
myAnnotation.subtitle=#"Globus";
[locations addObject:myAnnotation];
//second point
CLLocationCoordinate2D second;
second.latitude=13.0406527;
second.longitude=80.2437427;
myAnnotation= [[MKPointAnnotation alloc]init];
myAnnotation.coordinate=second;
myAnnotation.title=#"The Residency Towers";
myAnnotation.subtitle=#"Chennai";
[locations addObject:myAnnotation];
//third point
CLLocationCoordinate2D third;
third.latitude=13.040202;
third.longitude=80.240191;
myAnnotation= [[MKPointAnnotation alloc]init];
myAnnotation.coordinate=third;
myAnnotation.title=#"Rado Cool Zone";
myAnnotation.subtitle=#"Chennai";
[locations addObject:myAnnotation];
[self.mapView addAnnotations:locations];
title_para.text=[[NSString alloc]initWithString:#"The position displayed is Pondy Bazaar"];
coordsArray = malloc(sizeof(CLLocationCoordinate2D) * locations.count);
int i = 0;
for (CLLocation *loc in locations) {
coordsArray[i] = loc.coordinate;
i++;
}
routeLine = [MKPolyline polylineWithCoordinates:coordsArray
count:locations.count];
[mapView addOverlay:routeLine];
self.longitude_value.text=[NSString stringWithFormat:#"%f",center.longitude];
self.latitude_value.text=[NSString stringWithFormat:#"%f",center.latitude];
}
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
float latitude = [[view annotation ] coordinate].latitude;
float longitude = [[view annotation ] coordinate].longitude;
title_value=[[view annotation] title];
NSString * subtitle_val =[[view annotation] subtitle];
title_para.text=[NSString stringWithFormat:#"%#, %#",title_value,subtitle_val];
latitude_value.text=[NSString stringWithFormat:#"%f",latitude];
longitude_value.text=[NSString stringWithFormat:#"%f",longitude];
}
//Annotation view: icons
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id < MKAnnotation >)annotation
{
pinView = nil;
static NSString *identifier = #"Wifintech";
pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if ( pinView == nil )
pinView = [[MKPinAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:identifier];
if([[annotation title] isEqualToString:#"Wifin Technology"])
{
pinView.image = [UIImage imageNamed:#"car-side.png"];
pinView.canShowCallout = YES;
}
else if ([[annotation title] isEqualToString:#"The Residency Towers"])
{
pinView.image=[UIImage imageNamed:#"lorry-side.png"];
pinView.canShowCallout = YES;
}
else if([[annotation title] isEqualToString:#"Rado Cool Zone"])
{
//pinView.pinColor = MKPinAnnotationColorGreen;
pinView.canShowCallout = YES;
//pinView.animatesDrop = YES;
pinView.image = [UIImage imageNamed:#"car.png"];
}
else {
NSLog(#"Nothing");
}
return pinView;
}
#end
I'm using the following code to add annotations to a mapView. The problem is that the annotations don't appear in iOS 7 but do show up in iOS6
in my viewDidLoad method:
for (int i=0; i<[self.listingNodesArray count]; i++) {
MyAnnotation* annotation= [MyAnnotation new];
CLLocationCoordinate2D coordinate;
coordinate.latitude = [[[[self.listingNodesArray objectAtIndex:i] objectForKey:#"address"] objectForKey:#"lat"] doubleValue];
coordinate.longitude = [[[[self.listingNodesArray objectAtIndex:i] objectForKey:#"address"] objectForKey:#"lng"] doubleValue];
annView.image = [UIImage imageNamed:#"PIN_img.png"];// sets image for pin
annotation.coordinate = coordinate;
annotation.title = [[self.listingNodesArray objectAtIndex:i] objectForKey:#"title"];
annotation.subtitle = [[[self.listingNodesArray objectAtIndex:i] objectForKey:#"address"] objectForKey:#"address"];
NSNumber *listingIdNumber = [[self.listingNodesArray objectAtIndex:i] objectForKey:#"id"];
annotation.catListingMapId = listingIdNumber;
[self.topMapView addAnnotation: annotation];
}
[self.view addSubview:self.topMapView];
and viewForAnnotation:
- (MKAnnotationView *) mapView:(MKMapView *)mapingView viewForAnnotation:(id <MKAnnotation>) annotation {
annView = nil;
if(annotation != mapingView.userLocation)
{
static NSString *defaultPinID = #"";
annView = (MKAnnotationView *)[mapingView dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
if ( annView == nil )
annView = [[MKAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:defaultPinID] ;
UIButton* rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[rightButton setTitle:annotation.title forState:UIControlStateNormal];
// [rightButton addTarget:self
// action:#selector(showDetails:)
// forControlEvents:UIControlEventTouchUpInside];
annView.rightCalloutAccessoryView = rightButton;
annView.canShowCallout = YES;
}
return annView;
}
and my annotation file:
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface MapViewAnnotation : NSObject <MKAnnotation> {
NSString *title;
CLLocationCoordinate2D coordinate;
}
#property (nonatomic, copy) NSString *title;
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
- (id)initWithTitle:(NSString *)ttl andCoordinate:(CLLocationCoordinate2D)c2d;
#end
#import "MapViewAnnotation.h"
#implementation MapViewAnnotation
#synthesize title, coordinate;
- (id)initWithTitle:(NSString *)ttl andCoordinate:(CLLocationCoordinate2D)c2d {
title = ttl;
coordinate = c2d;
return self;
}
#end
As AnnaKarenina points out, you should set the image of your annotation view inside the viewForAnnotation method.
This may be somewhat "working" in iOS 6, but surely not completely. You're trying to set the annotation view's image before the annotation has been added to the map (and, thus, before the annotation view for this annotation has even been created). At best, you're setting the annotation view image for the prior annotation, not the one you think you are.
If your various annotations need to have unique images, go ahead and add a imageName property to your annotation, and then you can have your viewForAnnotation set the annotation view's image using that property. That's where you should be creating the UIImage for the annotation view. Frankly, I'd suggest you remove the annView ivar, to avoid the temptation to try to access the annotation view outside of viewForAnnotation.
By the way, the MapViewAnnotation class (referred to elsewhere as MyAnnotation) has lots of little issues of its own (e.g. your init is not calling super, you've defined the strings to be copy parameters but init is not doing that (such as title = [ttl copy]), its now advised that you don't explicitly create the ivars that back your properties, etc.), but those are unrelated to the broader issues regarding your annotation view images.
i've just created a new app, and i've implemented a map in a view.
I've created the map with some personal annotation but, when the user touch the pin, doesn't appear anything. I've tried to set the colour of the pin but nothing seem's to work. I need that the user, when touch the pin, can touch the disclosure button on the popup, to use "native maps".
Now, when i touch the pin, this one become darker, but nothing else.
Can someone help me? Please!!
io ne avrei bisogno, per far aprire poi un disclosure e far aprire l'app nativa mappe per creare il percorso!! Se ora tocco il pin, questo diventa piĆ¹ scuro ma non succede nulla! non riesco nemmeno ad agire su di loro per cambiare colore, immagini,.... ho guardato i tutorial ma non trovo l'errore!
header subclass
#import <Foundation/Foundation.h>
#import <MapKit/Mapkit.h>
#interface myAnnotation : NSObject <MKAnnotation>{
CLLocationCoordinate2D coordinate;
NSString *titolo;
NSString *sottotitolo;
}
#property(nonatomic,assign) CLLocationCoordinate2D coordinate;
#property(nonatomic,copy) NSString *titolo;
#property(nonatomic,copy) NSString *sottotitolo;
#end
implementation subclass
#implementation myAnnotation
#synthesize titolo;
#synthesize sottotitolo;
#synthesize coordinate;
-init{
return self;
}
#end
file .m view controller
CLLocation *userLoc = myMapView.userLocation.location;
CLLocationCoordinate2D userCoordinate = userLoc.coordinate;
NSLog(#"user latitude = %f",userCoordinate.latitude);
NSLog(#"user longitude = %f",userCoordinate.longitude);
myMapView.delegate=self;
NSMutableArray* annotations=[[NSMutableArray alloc] init];
CLLocationCoordinate2D theCoordinate1;
theCoordinate1.latitude = 45.;
theCoordinate1.longitude = 7.;
CLLocationCoordinate2D theCoordinate2;
theCoordinate2.latitude = 45.;
theCoordinate2.longitude = 12.;
CLLocationCoordinate2D theCoordinate3;
theCoordinate3.latitude = 45.;
theCoordinate3.longitude = 8.;
CLLocationCoordinate2D theCoordinate4;
theCoordinate4.latitude = 43.;
theCoordinate4.longitude = 7.;
myAnnotation* myAnnotation1=[[myAnnotation alloc] init];
myAnnotation1.coordinate=theCoordinate1;
myAnnotation1.titolo=#"xxxx";
myAnnotation1.sottotitolo=#"xxx";
myAnnotation* myAnnotation2=[[myAnnotation alloc] init];
myAnnotation2.coordinate=theCoordinate2;
myAnnotation2.titolo=#"yyyy";
myAnnotation2.sottotitolo=#"yyyy";
myAnnotation* myAnnotation3=[[myAnnotation alloc] init];
myAnnotation3.coordinate=theCoordinate3;
myAnnotation3.titolo=#"zzz";
myAnnotation3.sottotitolo=#"zzz";
myAnnotation* myAnnotation4=[[myAnnotation alloc] init];
myAnnotation4.coordinate=theCoordinate4;
myAnnotation4.titolo=#"kkk";
myAnnotation4.sottotitolo=#"kkk";
[myMapView addAnnotation:myAnnotation1];
[myMapView addAnnotation:myAnnotation2];
[myMapView addAnnotation:myAnnotation3];
[myMapView addAnnotation:myAnnotation4];
[annotations addObject:myAnnotation1];
[annotations addObject:myAnnotation2];
[annotations addObject:myAnnotation3];
[annotations addObject:myAnnotation4];
NSLog(#"%d",[annotations count]);
and then this snippet to show and personalize the pin
-(MKAnnotationView *)mapView:(MKMapView *)mV viewForAnnotation:
(id <MKAnnotation>)annotation {
MKPinAnnotationView *pinView = nil;
if(annotation != mapView.userLocation)
{
static NSString *defaultPinID = #"com.invasivecode.pin";
pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
if ( pinView == nil ) pinView = [[[MKPinAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:defaultPinID] autorelease];
pinView.pinColor = MKPinAnnotationColorPurple;
pinView.canShowCallout = YES;
pinView.animatesDrop = YES;
}
else {
[mapView.userLocation setTitle:#"I am here"];
}
return pinView;
}
The MKAnnotation protocol requires that the class responds to title and subtitle. The properties must be named exactly like that.
Although you can also have your own differently named properties for the same, the map view will not call them (it is looking for title and subtitle).
For the disclosure button, in viewForAnnotation, set the rightCalloutAccessoryView to a UIButton and respond to its tap in the calloutAccessoryControlTapped delegate method.
In that delegate method, you can access the annotation object using (myAnnotation *)view.annotation and then call openURL to open the Maps app.