I thought this would be something that's fairly common - but cant find it anywhere.
I know how to put images on a map. I use CSMapAnnotation found here http://spitzkoff.com/craig/?p=81.
I've pretty much taken the CSMapAnnotationTypeImage as an example and made a CSMapAnnotationTypeLabel but it keep just showing pins on the map and not the UILabel like I expected.
the header file
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface CSLabelAnnotationView : MKAnnotationView {
UILabel* _label;
}
#property(retain, nonatomic) UILabel* label;
#end
and the source file
#import "CSLabelAnnotationView.h"
#import "CSMapAnnotation.h"
#import "util.h"
#implementation CSLabelAnnotationView
#synthesize label = _label;
#define kWidth 120
#define kHeight 20
- (id)initWithAnnotation:(id <MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
self.frame = CGRectMake(0, 0, kWidth, kHeight);
self.backgroundColor = [UIColor clearColor];
CSMapAnnotation* csAnnotation = (CSMapAnnotation*)annotation;
_label = [[UILabel alloc] initWithFrame:self.frame];
_label.text=csAnnotation.title;
[self addSubview:_label];
return self;
}
#if 0
- (void)prepareForReuse
{
TGLog(#"");
[_imageView removeFromSuperview];
[_imageView release];
}
#endif
-(void) dealloc
{
[_label release];
[super dealloc];
}
#end
to add it to the map
annotation = [[[CSMapAnnotation alloc] initWithCoordinate:coordinate
annotationType:CSMapAnnotationTypeLabel
title:i.name
reuseIdentifier:#"ocualabel"] autorelease];
[_mapView addAnnotation:annotation];
and
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
MKAnnotationView* annotationView = nil;
if (annotation == mapView.userLocation){
return nil; //default to blue dot
}
// determine the type of annotation, and produce the correct type of annotation view for it.
CSMapAnnotation* csAnnotation = (CSMapAnnotation*)annotation;
TGLog(#"csAnnotation.annotationType %d", csAnnotation.annotationType);
if(csAnnotation.annotationType == CSMapAnnotationTypeStart ||
csAnnotation.annotationType == CSMapAnnotationTypeEnd)
{
NSString* identifier = #"Pin";
MKPinAnnotationView* pin = (MKPinAnnotationView*)[self.mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if(nil == pin)
{
pin = [[[MKPinAnnotationView alloc] initWithAnnotation:csAnnotation reuseIdentifier:identifier] autorelease];
}
[pin setPinColor:(csAnnotation.annotationType == CSMapAnnotationTypeEnd) ? MKPinAnnotationColorRed : MKPinAnnotationColorGreen];
annotationView = pin;
TGLog(#"csPin anno");
}
else if(csAnnotation.annotationType == CSMapAnnotationTypeImage)
{
NSString* identifier = csAnnotation.reuseIdentifier;
CSImageAnnotationView* imageAnnotationView = (CSImageAnnotationView*)[self.mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if(nil == imageAnnotationView)
{
imageAnnotationView = [[[CSImageAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier] autorelease];
imageAnnotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
}
TGLog(#"CSImage anno");
annotationView = imageAnnotationView;
} else if(csAnnotation.annotationType == CSMapAnnotationTypeLabel)
{
NSString* identifier = csAnnotation.reuseIdentifier;
CSLabelAnnotationView* labelAnnotationView = (CSLabelAnnotationView*)[self.mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if(nil == labelAnnotationView)
{
labelAnnotationView = [[[CSLabelAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier] autorelease];
}
TGLog(#"CSLabel anno");
} else {
TGLog(#"%d", csAnnotation.annotationType);
}
[annotationView setEnabled:YES];
[annotationView setCanShowCallout:YES];
return annotationView;
}
Can anyone help me with this? Or is there some other standard way to get UILabels on a map?
Found it.
Needed to add this in viewForAnnotation():
annotationView = labelAnnotationView;
Related
I think those two are related and I'm probably doing something wrong here:
First I'm overriding viewForAnnotation - in order to put my custom Image
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
// we avoid changing the user's location blue point
if([annotation isKindOfClass: [MKUserLocation class]]) {
return nil;
}
else
{
static NSString *SFAnnotationIdentifier = #"SFAnnotationIdentifier";
MKPinAnnotationView* pinAnnotationView = (MKPinAnnotationView*)[self.mapToPin dequeueReusableAnnotationViewWithIdentifier:SFAnnotationIdentifier];
if (!pinAnnotationView)
{
MKAnnotationView *annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation
reuseIdentifier:SFAnnotationIdentifier];
UIImage *littleImage = [UIImage imageNamed:#"littleImage.png"];
// You may need to resize the image here.
annotationView.image = littleImage;
pinAnnotationView.draggable = true;
pinAnnotationView.canShowCallout = true;
pinAnnotationView.animatesDrop = true;
return annotationView;
}
else
{
pinAnnotationView.annotation = annotation;
}
return pinAnnotationView;
}
}
It seems that pinAnnotationView's draggable, canShowCallout and animatesDrop are not working.
Then, I'm calling my method in my viewDidLoad to set the Annotation
-(void) setAnnotation:(NSString*)lat : (NSString*)lng
{
_mapAnnotation = [[MKPointAnnotation alloc] init];
CLLocationCoordinate2D coordinateForPin;
coordinateForPin.latitude = [lat floatValue];
coordinateForPin.longitude = [lng floatValue];
[_mapAnnotation setCoordinate:coordinateForPin];
[_mapAnnotation setTitle:#"sometitle"];
[_mapToPin addAnnotation:_mapAnnotation];
}
It seems that [_mapAnnotation setTitle:#"sometitle"]; is not working anymore when I'm overriding viewForAnnotation.
Is this the right way to go? I have been browsing but didn't find any solution that could help. Any feedback would be appreciated.
The problem is the accidental reference to the wrong variable name. The pinAnnotationView is nil inside that if statement, so setting properties with that nil reference is a NOOP.
if (!pinAnnotationView) {
MKAnnotationView *annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation
reuseIdentifier:SFAnnotationIdentifier];
...
annotationView.draggable = true; // not pinAnnotationView
annotationView.canShowCallout = true;
annotationView.animatesDrop = true;
return annotationView;
}
FWIW, if targeting iOS 10 or later, you can register your own class and move your annotation view configuration code into that subclass. Furthermore, in iOS 11 and later you do not need mapView:viewForAnnotation: at all.
For example, in iOS 11 and later:
// MyAnnotationView.h
#import MapKit;
NS_ASSUME_NONNULL_BEGIN
#interface MyAnnotationView : MKPinAnnotationView
#end
NS_ASSUME_NONNULL_END
and
// MyAnnotationView.m
#import "MyAnnotationView.h"
#implementation MyAnnotationView
- (instancetype)initWithAnnotation:(id<MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
if (self) {
...
self.draggable = true;
self.canShowCallout = true;
self.animatesDrop = true;
}
return self;
}
#end
And then, in your viewDidLoad, you register this class.
- (void)viewDidLoad {
[super viewDidLoad];
[self.mapView registerClass:[MyAnnotationView class] forAnnotationViewWithReuseIdentifier:MKMapViewDefaultAnnotationViewReuseIdentifier];
...
}
You can then remove the mapView:viewForAnnotation: implementation entirely.
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
I've been working on an app that has an MKMapView and I want to customize the pins with different images. I've already did that but with one image only, now I need to make a pin show an image and the other pin show another image. How can I do this? If helps, here's my code:
-(MKAnnotationView *)mapView:(MKMapView *)mV viewForAnnotation:(id <MKAnnotation>)annotation{
MKAnnotationView *pinView = nil;
if(annotation != _mapView.userLocation)
{
static NSString *defaultPinID;
pinView = (MKAnnotationView *)[_mapView dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
if ( pinView == nil )
pinView = [[MKAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:defaultPinID];
pinView.canShowCallout = YES;
pinView.image = [UIImage imageNamed:#"image1.jpg"];
}
else {
[_mapView.userLocation setTitle:#"Your Location"];
}
return pinView; }
I need the second and the third to show the same image but the first a different one
It follows like this:
- (void)viewWillAppear:(BOOL)animated {CLLocationCoordinate2D First; First.latitude = -12.098970; First.longitude = -77.034531; MKPointAnnotation *annotationPoint = [[MKPointAnnotation alloc] init]; annotationPoint.coordinate = First; annotationPoint.title = #"First"; annotationPoint.subtitle = #"Subtitle1"; [_mapView addAnnotation:annotationPoint];
CLLocationCoordinate2D Second; Second.latitude = -12.098299; Second.longitude = -77.068364; MKPointAnnotation *annotationPoint2 = [[MKPointAnnotation alloc] init]; annotationPoint2.coordinate = Second; annotationPoint2.title = #"Second"; annotationPoint2.subtitle = #"Subtitle2"; [_mapView addAnnotation:annotationPoint2];
CLLocationCoordinate2D Third; Third.latitude = -12.125888; Third.longitude = -77.023346; MKPointAnnotation *annotationPoint3 = [[MKPointAnnotation alloc] init]; annotationPoint3.coordinate = Third; annotationPoint3.title = #"Third"; annotationPoint3.subtitle = #"Subtitle3"; [_mapView addAnnotation:annotationPoint3];}
Make sure you have MapKit.framework and CoreLocation.framework in your project.
My custom pin images are 39 high by 32 wide. Have not tried other sizes but feel free to experiment. My 2 pin images are called pin1.png and pin2.png
Make sure you have your images named correctly to match what is in your code.
In my example I am not using current location but rather a static custom location (thought The Bahamas would be nice for this example). In your project you would of course you the Location Manager to get a user's current location.
I have tested my example and have successfully dropped 2 pins on the map with each pin having its own custom image.
It's not the cleanest code but I only had limited time to write it.
Here is the code for ViewController.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#import <CoreLocation/CoreLocation.h>
#interface ViewController : UIViewController <CLLocationManagerDelegate, MKMapViewDelegate>
#end
Here is the code for ViewController.h
#import "ViewController.h"
#import "MyAnnotation.h"
#import <MapKit/MapKit.h>
#interface ViewController ()
#property (strong, nonatomic) IBOutlet MKMapView *myMapView;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// setup the map view, delegate and current location
[self.myMapView setDelegate:self];
self.myMapView.mapType = MKMapTypeStandard;
CLLocationCoordinate2D myLocation = CLLocationCoordinate2DMake(25.085130,-77.331428);
[self.myMapView setCenterCoordinate:myLocation];
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(myLocation, 2000, 2000);
region.center = self.myMapView.centerCoordinate;
self.myMapView.showsUserLocation = YES;
[self.myMapView setRegion:region animated:YES];
[self dropPins];
}
-(void)dropPins {
NSMutableArray *annotationArray = [[NSMutableArray alloc] init];
CLLocationCoordinate2D location1 = CLLocationCoordinate2DMake(25.085130, -77.331428);
MyAnnotation *annotation1 = [[MyAnnotation alloc] initWithCoordinates:location1 image:#"pin1.png"];
[annotationArray addObject:annotation1];
[self.myMapView addAnnotations:annotationArray];
[annotationArray removeAllObjects];
CLLocationCoordinate2D location2 = CLLocationCoordinate2DMake(25.085130, -77.336428);
MyAnnotation *annotation2 = [[MyAnnotation alloc] initWithCoordinates:location2 image:#"pin2.png"];
[annotationArray addObject:annotation2];
[self.myMapView addAnnotations:annotationArray];
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
static NSString *identifier = #"MyLocation";
if ([annotation isKindOfClass:[MyAnnotation class]])
{
MKPinAnnotationView *annotationView = (MKPinAnnotationView *)[self.myMapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView == nil)
{
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
} else
{
annotationView.annotation = annotation;
}
annotationView.enabled = YES;
annotationView.canShowCallout = NO;
if([[(MyAnnotation *)annotationView.annotation image] isEqualToString:#"pin1.png"])
annotationView.image = [UIImage imageNamed:#"pin1.png"];
if([[(MyAnnotation *)annotationView.annotation image] isEqualToString:#"pin2.png"])
annotationView.image = [UIImage imageNamed:#"pin2.png"];
return annotationView;
}
return nil;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Here is the code for MyAnnotation.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface MyAnnotation : NSObject <MKAnnotation>
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy, readonly) NSString *image;
-(id)initWithCoordinates:(CLLocationCoordinate2D) paramCoordinates
image:(NSString *) paramImage;
#end
Here is the code for MyAnnotation.m
#import "MyAnnotation.h"
#implementation MyAnnotation
-(id)initWithCoordinates:(CLLocationCoordinate2D)paramCoordinates
image:(NSString *)paramImage
{
self = [super init];
if(self != nil)
{
_coordinate = paramCoordinates;
_image = paramImage;
}
return (self);
}
#end
This is how I'm adding an annotation on MapKit:
MKPointAnnotation *point = [[MKPointAnnotation alloc] init];
point.coordinate = zoomLocation;
point.title = #"Where am I?";
point.subtitle = #"I'm here!!!";
[self.mapView addAnnotation:point ];
How can change the image of the annotation?
Create a custom annotation class.
in CustomAnnotation.h
#import <MapKit/MapKit.h>
#interface CustomAnnotation : MKAnnotationView
#end
in CustomAnnotation.m
#import "CustomAnnotation.h"
#implementation CustomAnnotation
-(id)initWithAnnotation:(id<MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
if (self != nil) {
CGRect frame = self.frame;
frame.size = CGSizeMake(46.0, 49.0);
self.frame = frame;
self.backgroundColor = [UIColor clearColor];
self.centerOffset = CGPointMake(-5, -5);
}
return self;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
[[UIImage imageNamed:#"IMAGE_NAME"] drawInRect:rect];
}
in our mapview delegate method viewForAnnotation
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
static NSString *defaultPinId = #"Pin";
CustomAnnotation *pinView = (CustomAnnotation *)[mapView dequeueReusableAnnotationViewWithIdentifier:defaultPinId];
if (pinView == nil) {
pinView = [[CustomAnnotation alloc]initWithAnnotation:annotation reuseIdentifier:defaultPinId];
}
else {
pinView.annotation = annotation;
}
return pinView;
}
Try this,
MKPointAnnotation *point = [[MKPointAnnotation alloc] init];
[[self.mapView viewForAnnotation:point] setTag:1];
UIImage *image = [UIImage imageNamed:#"pinIcon.png"];
[[self.mapView viewForAnnotation:point] setImage:image];