How to long press and lift an MKAnnotationView - iphone

I added an MKAnnotationView on an MKMapView, when I long press on it, I hope it can be lifted and move to another location.
How to? Special thanks!

For iOS 4.0 or later
From Apple's Location Awareness Guide > Marking Your Annotation View as Draggable:
To implement minimal support for dragging, you must do the following:
In your annotation objects, implement the setCoordinate: method to allow the map view to update the annotation’s coordinate point.
When creating your annotation view, set its draggable property to YES.
Implementing setCoordinate:
#import <MapKit/MapKit.h>
#interface mapAnnotation : NSObject<MKAnnotation> {
double latitude, longitude;
}
#end
#implementation mapAnnotation
- (void)setCoordinate:(CLLocationCoordinate2D)coordinate{
latitude = coordinate.latitude;
longitude = coordinate.longitude;
}
-(CLLocationCoordinate2D)coordinate {
CLLocationCoordinate2D coordinate = {latitude,longitude};
return coordinate;
}
// ...
#end
Creating the annotation view in the view controller:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
static NSString * const identifier = "identifier";
MKPinAnnotationView *pinView = (MKPinAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (pinView ==nil) {
pinView = [[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:identifier];
pinView.animatesDrop = YES;
pinView.draggable = YES;
}
return pinView;
}
For any iOS version
Track the user touches and move the pin view accordingly. Look at DDAnnotationView at the bottom of this page: http://digdog.tumblr.com/post/252784277/mapkit-annotation-drag-and-drop-with-callout-info
When the user stops dragging convert the view position to map coordinates and change the annotation coordinates. Throw in some animations for the pin.
Here is a working example : https://github.com/digdog/MapKitDragAndDrop Try adding the classes DDAnnotationView and DDAnnotation to your project, and using the MKMapViewDelegate methods below this line.

Related

iphone: How to show different image for every pin point on MapKit?

I want to put Start and End image with overlay in an iPhone/iPad application. I have start and end Lattitude and Longitude values and want to draw overlay between start and end points and put start image on Start point and End Image on End point.
I have googled but What I found is MapKit gets one image and set it on both Start and End points, could not find any help for 2nd image.
like
annotationView.image=[UIImage imageNamed:#"parkingIcon.png"];
It only set one image for both start and end points. But I want to put different images for both points.
Please help.
I got that ... thanks for all who tried to help me out. the Complete solution is
Create a class
#interface MyAnnotationClass : NSObject <MKAnnotation> {
NSString *_name;
NSString *_description;
CLLocationCoordinate2D _coordinate;
}
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *description;
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
-(id) initWithCoordinate:(CLLocationCoordinate2D) coordinate;
ViewDidLoad method code :
mapView.delegate = self;
//Initialize annotation
MyAnnotationClass *commuterLotAnnotation=[[MyAnnotationClass alloc] initWithCoordinate:CLLocationCoordinate2DMake( 39.047752, -76.850388)];
commuterLotAnnotation.name = #"1";
MyAnnotationClass *overflowLotAnnotation=[[MyAnnotationClass alloc] initWithCoordinate:CLLocationCoordinate2DMake( 39.047958, -76.852520)];
overflowLotAnnotation.name = #"2";
//Add them to array
self.myAnnotations=[NSArray arrayWithObjects:commuterLotAnnotation, overflowLotAnnotation, nil];
//Release the annotations now that they've been added to the array
[commuterLotAnnotation release];
[overflowLotAnnotation release];
//add array of annotations to map
[mapView addAnnotations:_myAnnotations];
viewForAnnotation code :
-(MKAnnotationView *)mapView:(MKMapView *)MapView viewForAnnotation:(id<MKAnnotation>)annotation{
static NSString *parkingAnnotationIdentifier=#"ParkingAnnotationIdentifier";
if([annotation isKindOfClass:[MyAnnotationClass class]]){
//Try to get an unused annotation, similar to uitableviewcells
MKAnnotationView *annotationView=[MapView dequeueReusableAnnotationViewWithIdentifier:parkingAnnotationIdentifier];
//If one isn't available, create a new one
if(!annotationView){
annotationView=[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:parkingAnnotationIdentifier];
if([((MyAnnotationClass *)annotation).name isEqualToString: #"1"]){
annotationView.image=[UIImage imageNamed:#"passenger.png"];
}
else if([((MyAnnotationClass *)annotation).name isEqualToString: #"2"]){
annotationView.image=[UIImage imageNamed:#"place.png"];
}
}
return annotationView;
}
return nil;
}
This is how you can add separate image for every point on MapKit.
you have to use the -(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation; , a MKAnnotation delegate method
wich allow you to set a given annotation on the map.
This method is returned for every annotations, you just have to find the end and start annotations and set image to it
First, you have to implement the MKMapViewDelegate protocol and set the delegate class to your map.
In the MKMapView Delegate, declare the method:
- (MKAnnotationView *)mapView:(id)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
if (annotation == [mapView userLocation]) // The user's blue point : no customisation.
return nil;
MKAnnotationView * annView = [mapView dequeueReusableAnnotationViewWithIdentifier:/* your specific identifier */];
if (annView == nil) {
annView = [[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:/* your specific identifier */] autorelease];
}
annView.image = /* your image */;
return annView
}
But you have to detect if the annotation is the starting one or the ending one. You can set a specific title to easily retrieve it (like #"start") or subclass your MKAnnotations by adding a discriminating property.

How to show a call out for object that implements MkAnnotation protcol ?

I have an object that implements the MKAnnotation protocol:
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface VoiceMemoryAnnotation : NSObject <MKAnnotation> {
NSString * blobkey;
}
#property (nonatomic, retain) NSString * blobkey;
-(id)initWithBlobkey:(NSString *) key andCoordinate:(CLLocationCoordinate2D) c;
#end
Adding this object a map works perfectly since I can see the red pins being dropped. However, the problem arises when I want to set this object to show a callout.
I cannot do annotation.showCallOut=YES because an "MkAnnotation" does not have this property, but a MkAnnotationView does. How do I get around this?
I tried to implement the map callback "viewForAnnotation" to check for "VoiceMemoryAnnotation" and I try to return a new "MkAnnotationView" and set it's callout = YES, but I start to get a segmentation fault when I do this.
Any ideas what I"m doing wrong?
First you need to create your annotation object (the one that implements the MKAnnotation protocol) and add it to your map using something like
VoiceMemoryAnnotation*VMA = [[VoiceMemoryAnnotation alloc] init];
VMA.title = #"Title String";
VMA.subtitle = #"Subtitle String";
[self.mapView addAnnotation:VMA];
That will automatically call the following method which you will need to implement:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
MKPinAnnotationView*singleAnnotationView = [[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:nil];
singleAnnotationView.canShowCallout = YES;
return singleAnnotationView;
}
In this implementation MKAnnotationView won't work, it needs to be MKPinAnnotationView.
I'm not sure I completely understand your question, but I wonder, is MKMapViews's - (void)selectAnnotation:(id <MKAnnotation>)annotation animated:(BOOL)animated what you're looking for?

MKAnnotationView and passing data to another function

Actually I'm developing something in my app which displays annotations within my MKMapView and those annotations should be clickable and pass a param to a method.
The problem is for now how to pass the parameter to the next function when tapping the disclosure button.
For the formatting of the annotations I use the - (MKAnnotationView *)mapView:(MKMapView *)aMapView viewForAnnotation:(id <MKAnnotation>)annotation method to display the disclosure button. But the problem is, how to get values out of the Annotation object which is displayed at the moment?
My code for the Annotation (only the relevant part):
#synthesize coordinate, id, title, subtitle;
+ (id)annotationWithCoordinate:(CLLocationCoordinate2D)coordinate {
return [[[[self class] alloc] initWithCoordinate:coordinate] autorelease];
}
- (id)initWithCoordinate:(CLLocationCoordinate2D)coordinate {
self = [super init];
if(nil != self) {
self.coordinate = coordinate;
}
return self;
}
And when touching the disclosure button the method - (void)displayInfo:(NSNumber *) anId; should be called with handing over Annotation.id as the anId param.
The disclosure button is generated with that code:
UIButton *disclosureButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
dropPin.rightCalloutAccessoryView = disclosureButton;
dropPin.animatesDrop = YES;
dropPin.canShowCallout = YES;
So - the question:
How can I pass that param to the function OR read the id of the clicked Annotation?
I thought I could do the handling of the tap on disclosureButton either with a selector for UIButton (but - how to pass the id?) or with - (void)mapView:(MKMapView *)mapView
annotationView:(MKAnnotationView *)view
calloutAccessoryControlTapped:(UIControl *)control (again - how to pass the id?).
I hope you know what I mean and very big thanks in advance for your answers! :)
In calloutAccessoryControlTapped the annotation is available via the view:
MKAnnotation *annotation = view.annotation;

Display annotation in Map kit

I am working with Mapkit and I have to show annotations in the map but I'm not able to display the annotation. Here's my code:
#interface MyMapView : UIViewController <MKAnnotation,MKMapViewDelegate>{
MKMapView *Obj_Map_View;
MKPlacemark *pmark;
MKReverseGeocoder *geocoder1;
}
#end
#import "MyMapView.h"
#implementation MyMapView
- (id)init {
if (self = [super init]) {
}
return self;
}
- (void)loadView {
[super loadView];
Obj_Map_View = [[MKMapView alloc]initWithFrame:self.view.bounds];
Obj_Map_View.showsUserLocation =YES;
Obj_Map_View.mapType=MKMapTypeStandard;
[self.view addSubview:Obj_Map_View];
Obj_Map_View.delegate = self;
CLLocationCoordinate2D cord = {latitude: 19.120000, longitude: 73.020000};
MKCoordinateSpan span = {latitudeDelta:0.3, longitudeDelta:0.3};
MKCoordinateRegion reg= {cord,span};
[Obj_Map_View setRegion:reg animated:YES];
//[Obj_Map_View release];
}
- (NSString *)subtitle{
return #"Sub Title";
}
- (NSString *)title{
return #"Title";
}
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation
{
MKPinAnnotationView *annov = [[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:#"Current location"];
annov.animatesDrop = TRUE;
[annotation title]==#"Current location";
annov.canShowCallout = YES;
[annov setPinColor:MKPinAnnotationColorGreen];
return annov;
}
The above code works fine and displays a map but not with annotation.
Typically, the class that conforms to the MKAnnotation protocol isn't the view controller, it's a data class.
You'll need to create another class, which I'll call "MyLandmarks" for the example.
#interface MyLandmarks : NSObject <MKAnnotation>
// Normally, there'd be some variables that contain the name and location.
// And maybe some means to populate them from a URL or a database.
// This example hard codes everything.
#end
#implementation MyLandmarks
-(NSString*)title {
return #"'ere I am, J.H.";
}
-(NSString*)subtitle {
return #"The ghost in the machine.";
}
-(CLLocationCoordinate2D) coordinate {
CLLocationCoordinate2D coord = {latitude: 19.120000, longitude: 73.020000};
return coord;
}
#end
Then, somewhere appropriate in your MyMapView class add:
MyLandmark *landmark = [[[MyLandmark alloc]init]autorelease];
[Obj_Map_View addAnnotation:landmark];
A couple other bits that other Objective-C developers working with you will appreciate:
To avoid confusion, don't call the class MyMapView if it descends from a UIViewController. Call it MyMapViewController, instead.
Classes start with a capital letter, variables start lowercase. Both are CamelCased. Obj_Map_View should be objMapView.
To add annotation use : addAnnotation:
read about it here

Mapkit with multi annotation (callout), mapping the next view

Wanted some help with a problem with mapkit I am facing. Should be a silly problem or I have missed out something while going through the mapkit framework.
Here is the senario.
I am placing multiple annotation on the map when the user performs some search like pizza.
Added button for the right annotation view, on click which opens a next detail view. The problem is how to send some information to the next view, for example I add index to annotations while creating them, now I want to access this information from annotation, and pass it to the next view via the selector set on the button.
I have checked all the mapkit delicate, but don't find a one where I can map this information with the next view and annotation.
Hope I have not confused you guys in my question. Please let me know I will reframe it.
Thaking in advance.
When you create the UIButton for the annotation, set the tag property (tag is an NSInteger property of UIView) to an id or array index that identifies the relevant object. You can then retrieve that tag value from the sender parameter to your selector.
Edit: here's some sample code.
You create your annotation view and associate the button in your delegate's -mapView:viewForAnnotation: method:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
// Boilerplate pin annotation code
MKPinAnnotationView *pin = (MKPinAnnotationView *) [self.map dequeueReusableAnnotationViewWithIdentifier: #"restMap"];
if (pin == nil) {
pin = [[[MKPinAnnotationView alloc] initWithAnnotation: annotation reuseIdentifier: #"restMap"] autorelease];
} else {
pin.annotation = annotation;
}
pin.pinColor = MKPinAnnotationColorRed
pin.canShowCallout = YES;
pin.animatesDrop = NO;
// now we'll add the right callout button
UIButton *detailButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
// customize this line to fit the structure of your code. basically
// you just need to find an integer value that matches your object in some way:
// its index in your array of MKAnnotation items, or an id of some sort, etc
//
// here I'll assume you have an annotation array that is a property of the current
// class and we just want to store the index of this annotation.
NSInteger annotationValue = [self.annotations indexOfObject:annotation];
// set the tag property of the button to the index
detailButton.tag = annotationValue;
// tell the button what to do when it gets touched
[detailButton addTarget:self action:#selector(showDetailView:) forControlEvents:UIControlEventTouchUpInside];
pin.rightCalloutAccessoryView = detailButton;
return pin;
}
Then in your action method, you'll unpack the value from tag and use it to display the right detail:
-(IBAction)showDetailView:(UIView*)sender {
// get the tag value from the sender
NSInteger selectedIndex = sender.tag;
MyAnnotationObject *selectedObject = [self.annotations objectAtIndex:selectedIndex];
// now you know which detail view you want to show; the code that follows
// depends on the structure of your app, but probably looks like:
MyDetailViewController *detailView = [[MyDetailViewController alloc] initWithNibName...];
detailView.detailObject = selectedObject;
[[self navigationController] pushViewController:detailView animated:YES];
[detailView release];
}
In the Annotation view, is it possible to grab,say, the Title or Subtitle or any other information you used while creating pins? What i am looking to do is have a certain image popup in the annotation based on one of those variables.
#import "MapPin.h"
#implementation MapPin
#synthesize coordinate;
#synthesize title;
#synthesize subtitle;
#synthesize indexnumber;
#synthesize imageFile;
-(id)initWithCoordinates:(CLLocationCoordinate2D)location
placeName: placeName
description:description
indexnum:indexnum
imageFileLoc:imageFileLoc{
self = [super init];
if (self != nil) {
imageFile=imageFileLoc;
[imageFile retain];
indexnumber=indexnum;
[indexnumber retain];
coordinate = location;
title = placeName;
[title retain];
subtitle = description;
[subtitle retain];
}
return self;
}
-(void)addAnnotations {
// Normally read the data for these from the file system or a Web service
CLLocationCoordinate2D coordinate = {35.9077803, -79.0454936};
MapPin *pin = [[MapPin alloc]initWithCoordinates:coordinate
placeName:#"Keenan Stadium"
description:#"Tar Heel Football"
indexnum:#"1"
imageFileLoc:#"owl.jpg"];
[self.map addAnnotation:pin];
Another option:
You can implement these methods:
- (void)mapView:(MKMapView *)mapView didSelectAnnotation:(MKAnnotationView *)view;
- (void)mapView:(MKMapView *)mapView didDeselectAnnotation:(MKAnnotationView *)view;