In which way could I call the function that automatically opens my annotation (with title, subtitle, etc), rather than to touch on the annotation on the mapview?
Implement MKMapViewDelegate delegate;
Implement - (MKAnnotationView *) mapView: (MKMapView *) mapView_ viewForAnnotation: (id <MKAnnotation>) annotation_;; for example like this:
- (MKAnnotationView *) mapView: (MKMapView *) mapView_ viewForAnnotation: (id <MKAnnotation>) annotation_ {
MKPinAnnotationView *pin = (MKPinAnnotationView *) [self.mapView dequeueReusableAnnotationViewWithIdentifier: #"YourPinId"];
if (pin == nil) {
pin = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation_ reuseIdentifier: #"YourPinId"] autorelease];
}
else {
pin.annotation = annotation_;
}
pin.pinColor = MKPinAnnotationColorRed;
[pin setCanShowCallout:YES];
pin.animatesDrop = YES;
return pin;
}
Show the pin after the map has finished loading:
- (void) dropPin {
[mapView addAnnotation:self.annotation];
[mapView selectAnnotation:self.annotation animated:YES];
}
- (void) mapViewDidFinishLoadingMap: (MKMapView *) mapView_ {
// if done loading, show the call out
[self performSelector:#selector(dropPin) withObject:nil afterDelay:0.3];
}
This code has a property called annotation which implements MKAnnotation. Also, it animates the pin drop too, but it should be fairly self-explaining.
HTH.
Alfons answered the question but if you are looking for what exactly automatically opens the callout, it's this part:
[mapView selectAnnotation:annotation animated:YES];
Related
I am new to mapkit in objective-c. I am able to add custom annotation in mapview.
i need to place custom callout view like below image
.
But i didn't get how can i design callout view like this.
i know i need to add callout in view for annotation method.
- (MKAnnotationView *)mapView:(MKMapView *)map viewForAnnotation:(id <MKAnnotation>)annotation
{
static NSString *AnnotationViewID = #"annotationViewID";
MKAnnotationView *annotationView = (MKAnnotationView *)[mapview dequeueReusableAnnotationViewWithIdentifier:AnnotationViewID];
if (annotationView == nil)
{
annotationView = [[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:AnnotationViewID] autorelease];
}
annotationView.image = [UIImage imageNamed:#"blue_without_pin.png"];
annotationView.annotation = annotation;
return annotationView;
}
Basically what you want to do is add a new custom view by implementing the following method in your MKMapViewDelegate instance :
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view {
CGPoint annotationCenter = [mapView convertCoordinate:view.annotation.coordinate toPointToView:mapView];
[self displayCustomCalloutForAnnotation:view.annotation center:annotationCenter];
}
#interface CustomAnnotaionView : MKAnnotationView
#property (strong, nonatomic) UIView *calloutView;
-(void)setSelected:(BOOL)selected animated:(BOOL)animated;
#end
#implementation CustomAnnotaionView
#synthesize calloutView = _calloutView;
-(void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
if(selected)
{
[self.calloutView setFrame:CGRectMake(30, 130, 250, 135)];
[self.calloutView sizeToFit];
self.calloutView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"popup.png"]];
[self addSubview:self.calloutView];
}
else
{
[self.calloutView removeFromSuperview];
}
}
-(void)didAddSubview:(UIView *)subview
{
if([[[subview class]description]isEqualToString:#"UICalloutView"])
{
[subview removeFromSuperview];
}
}
Use this customAnnotationView class in MapView delegate method,
- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id
<MKAnnotation>)annotation
{
if([annotation isKindOfClass:[MapAnnotation class]])
{
CustomAnnotationView *annotationView = (CustomAnnotaionView*)[theMapView dequeueReusableAnnotationViewWithIdentifier:#"CustomAnnotationView"];
if(!annotationView)
{
MapAnnotation *annotation = (MapAnnotation *)annotation;
annotationView = [[CustomAnnotaionView alloc]initWithAnnotation:annotation reuseIdentifier:#"CustomAnnotationView"];
[annotationView setCanShowCallout:YES];
}
else
annotationView.annotation = annotation;
return annotationView;
}
return nil;
}
One solution for this would be to detect the exact coordinate of the annotation selected relative to the parent UIView and then drawing a UIView directly on top of the MKMapView.
Here's a snippet that might help:
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
CGPoint pointInMap = [self.mapView convertCoordinate:buildingAnnotation.coordinate
toPointToView:self.view];
// Hide the default callout generated by MKMapView
[mapView deselectAnnotation:view.annotation animated:NO];
// Create a custom callout view and center the arrow on the pointInMap CGPoint
}
You must implement a callout view yourself, and use
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
this delegate to make the callout view showing at right place,
IOS have not any method to custom callout view yet
I want to design a dynamic pin annotation on Google map in iPhone such that user can drag the pin by taping on it and place this pin on Google maps to set the location of its choice. After user puts the pin I want to get the location coordinates and location name corresponding to that pin.
Any suggestion how to develop this in iPhone.Thanks
In your view controller you have to implement methods from MKMapViewDelegate protocol and LongPressGestureAware (which I created) for dropping the pin on the map when the user taps and hold on the screen.
Your annotation should implement MKAnnotation and MKReverseGeocoderDelegate protocols.
I paste some of my code which can help you:
SimpleMapAnnotationViewController.h:
#interface SimpleMapAnnotationViewController : TTViewController<LongPressGestureAware, MKMapViewDelegate> {
SimpleMapAnnotation *_dropPin;
MKPinAnnotationView *_pinView;
}
SimpleMapAnnotationViewController.m :
#pragma mark -
#pragma mark LongPressGestureAware
-(void) initLongPressGestureRecognizer {
UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleLongPressGesture:)];
[self.map addGestureRecognizer:longPressGesture];
[longPressGesture release];
}
-(void)handleLongPressGesture:(UIGestureRecognizer*)sender {
if([sender isMemberOfClass:[UILongPressGestureRecognizer class]] && (sender.state == UIGestureRecognizerStateEnded || sender.state == UIGestureRecognizerStateBegan)) {
[self.map removeGestureRecognizer:sender]; //avoid multiple pins to appear when holding on the screen
}
CGPoint point = [sender locationInView:self.map];
CLLocationCoordinate2D theCoordinate = [self.map convertPoint:point toCoordinateFromView:self.map];
self.dropPin = [[[SimpleMapAnnotation alloc] initWithCoordinate:theCoordinate] autorelease];
[self.map addAnnotation:self.dropPin];
[self performSelector:#selector(selectInitialAnnotation) withObject:nil afterDelay:0.5];
}
-(void)selectInitialAnnotation {
[self.map selectAnnotation:[self.map.annotations objectAtIndex:0] animated:YES];
}
#pragma mark -
#pragma mark MKMapViewDelegate
- (MKAnnotationView *) mapView: (MKMapView *) mapView viewForAnnotation: (id<MKAnnotation>) annotation {
if (annotation == self.map.userLocation){
return nil; //default to blue dot
}
MKPinAnnotationView *pin = (MKPinAnnotationView *) [mapView dequeueReusableAnnotationViewWithIdentifier: #"annotation_ID"];
if (pin == nil) {
pin = [[[MKPinAnnotationView alloc] initWithAnnotation: annotation reuseIdentifier: #"annotation_ID"] autorelease];
} else {
pin.annotation = annotation;
}
pin.canShowCallout = YES;
pin.draggable = YES;
pin.animatesDrop = YES;
pin.pinColor = MKPinAnnotationColorGreen;
self.pinView = pin;
self.dropPin.pinView = self.pinView;
return pin;
}
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)annotationView didChangeDragState:(MKAnnotationViewDragState)newState fromOldState:(MKAnnotationViewDragState)oldState {
//NSArray *annotations = self.map.annotations;
if (oldState == MKAnnotationViewDragStateDragging) {
SimpleMapAnnotation *annotation = (SimpleMapAnnotation *)annotationView.annotation;
[annotation updateSubtitle];
}
if(newState == MKAnnotationViewDragStateEnding) {
NSLog(#"drag finish");
}
}
SimpleMapAnnotation.h
#interface SimpleMapAnnotation : NSObject <MKAnnotation, MKReverseGeocoderDelegate> {
CLLocationCoordinate2D _coordinate;
NSString *_title;
NSString *_subtitle;
MKPinAnnotationView *_pinView;
}
SimpleMapAnnotation.m
- (id)initWithCoordinate:(CLLocationCoordinate2D)coordinate {
self.coordinate = coordinate;
MKReverseGeocoder *geoCoder = [[MKReverseGeocoder alloc] initWithCoordinate:self.coordinate];
geoCoder.delegate = self;
[geoCoder start];
self.subtitle = [NSString stringWithFormat:#"%f %f", self.coordinate.latitude, self.coordinate.longitude];
return self;
}
#pragma mark -
#pragma mark MKReverseGeocoderDelegate
// this delegate is called when the reverseGeocoder finds a placemark
- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFindPlacemark:(MKPlacemark *)placemark
{
MKPlacemark * myPlacemark = placemark;
NSString *address = [myPlacemark.addressDictionary objectForKey:(NSString*) kABPersonAddressStreetKey];
self.title = address;
}
// this delegate method is called if an error occurs in locating your current location
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(#"locationManager:%# didFailWithError:%#", manager, error);
}
// this delegate is called when the reversegeocoder fails to find a placemark
- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFailWithError:(NSError *)error
{
//invalid place
}
You can read up the documentation on Maps Annonations.
Here is the link
You can get location coordinates from any point in the MapView using the convertPoint:toCoordinateFromView: method of MapView
I have a map with only one annotation.I created a simple class which I want it to show when the user clicks on the annotation.The problem is that when I click on the annotation nothing happens.
Here is my code:
- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFindPlacemark:(MKPlacemark *)placemark{
NSLog(#"Reverse Geocoder completed");
mPlacemark=placemark;
[mapView addAnnotation:placemark];
}
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation{
MKPinAnnotationView *annView=[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"currentloc"];
annView.animatesDrop=TRUE;
//create UIButton for annotation
UIButton *detailButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
//NSInteger annotationValue = [self.annotations indexOfObject:annotation];
[detailButton addTarget:self action:#selector(showDetailView:) forControlEvents:UIControlEventTouchUpInside];
annView.rightCalloutAccessoryView=detailButton;
return annView;
}
-(void)showDetailView:(id)sender{
NSLog("inside the stupid method");
MyDetailViewController *detailView=[[MyDetailViewController alloc] initWithNibName:#"MyDetailViewController" bundle:nil];
[[self navigationController] pushViewController:detailView animated:YES];
[detailView release];
}
My showDetailView function never gets called.Please help me.I'm new to iphone and I might forget a simple thing.Thanks
Still not working!!!!
First, check that the map view's delegate is set otherwise your viewForAnnotation method will not get called.
Next, the accessory button appears on the annotation's callout which will only appear if you set canShowCallout:
annView.canShowCallout = YES;
Next, instead of using your own method to handle the button action, it's much better to use the map view's own calloutAccessoryControlTapped delegate method which gets called when tapping an accessory:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
NSLog(#"inside the stupid method");
//Here, the annotation tapped can be accessed using view.annotation
}
Remove the [detailButton addTarget... line from viewForAnnotation.
Also note your NSLog in the showDetailView method is missing the leading # which will result in a crash when the method does get called.
Another thing is you should use dequeueReusableAnnotationViewWithIdentifier in viewForAnnotation to enable annotation view re-use.
Example as requested:
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation
{
static NSString *annReuseId = #"currentloc";
MKPinAnnotationView *annView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:annReuseId];
if (annView == nil)
{
annView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:annReuseId];
annView.animatesDrop = YES;
annView.canShowCallout = YES;
UIButton *detailButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
annView.rightCalloutAccessoryView=detailButton;
}
else {
annView.annotation = annotation;
}
return annView;
}
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
NSLog(#"calloutAccessoryControlTapped: annotation = %#", view.annotation);
MyDetailViewController *detailView=[[MyDetailViewController alloc] initWithNibName:#"MyDetailViewController" bundle:nil];
//here, can set annotation info in some property of detailView
[[self navigationController] pushViewController:detailView animated:YES];
[detailView release];
}
Ok. If you are using storyboards, you need to create a segue between the map view controller and then where the below code exists:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
NSLog(#"calloutAccessoryControlTapped: annotation = %#", view.annotation);
MyDetailViewController *detailView=[[MyDetailViewController alloc] initWithNibName:#"MyDetailViewController" bundle:nil];
//here, can set annotation info in some property of detailView
[[self navigationController] pushViewController:detailView animated:YES];
[detailView release];
}
Use the following code instead:
NSLog(#"calloutAccessoryControlTapped: annotation = %#", view.annotation);
[self performSegueWithIdentifier:#"DetailViewController" sender:self];
This will give you the movement to the detailed view.
write your function like this:
-(IBAction)showDetailView:(id)sender{
NSLog("inside the stupid method");
MyDetailViewController *detailView=[[MyDetailViewController alloc] initWithNibName:#"MyDetailViewController" bundle:nil];
[[self navigationController] pushViewController:detailView animated:YES];
[detailView release];
}
you have to set the map delegate to self like this,
mapview.delegate=self; (if your calling the method in same page)
if you don't assign the delegate in your code the respective methods won't be called, so check if you have the above mentioned line.
Replace -(IBAction)showDetailView:(UIView*)sender with following...
-(void)showDetailView:(id)sender
In my MapView i have pin dropped on certain locations and now i want to display some title on these pin annotation and make it clickable so that on click i can push another view.
Please help !!!!
This is the code snippet:
Custom Annotation View using this method
- (MKAnnotationView *)mapView:(MKMapView *)map viewForAnnotation:(id <MKAnnotation>)annotation
{
if (annotation == mapView.userLocation) return nil;
MKPinAnnotationView *pin = (MKPinAnnotationView *) [mapView dequeueReusableAnnotationViewWithIdentifier: #"asdf"];
if (pin == nil)
{
pin = [[[MKPinAnnotationView alloc] initWithAnnotation: annotation reuseIdentifier: #"asdf"] autorelease];
}
else
{
pin.annotation = annotation;
}
pin.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
pin.pinColor = MKPinAnnotationColorRed;
pin.animatesDrop = YES;
[pin setEnabled:YES];
[pin setCanShowCallout:YES];
return pin;
}
Delegate method which get's called when you tap on the button
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
//DetailPg=[[BookMarksDetail alloc]initWithNibName:#"BookMarksDetail" bundle:nil];
//DetailPg.selectRest =[view.annotation title]; //In addition, to get the Title of the AnnotationView.
//DetailPg.m=1;
//[self.navigationController pushViewController:DetailPg animated:YES];
//[DetailPg release];
}
The answers to this are all provided in Apples Docs I suggest you read them more closely. However to have a call out all you need to do is set the title property.
For buttons and such you need to use the MapView Delegate method:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
_pin.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
Apple suggests reusing annotations this is something you will need to take into account.
I am trying to implement a draggable "pin" (actually a custom icon) in a map view. This is the delegate code that I have:
-(MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
MKAnnotationView *aView;
aView=(MKAnnotationView *) [mvMap dequeueReusableAnnotationViewWithIdentifier:annotation.title];
if (aView==nil)
aView=[[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:annotation.title] autorelease];
else
aView.annotation=annotation;
[aView setImage:[UIImage imageNamed:selIcon]];
aView.canShowCallout=TRUE;
[aView setDraggable:YES];
return aView;
}
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views {
MKAnnotationView *aV;
for (aV in views) {
CGRect endFrame = aV.frame;
int xDelta=0;
xDelta=sIcons.selectedSegmentIndex*61+22;
aV.frame = CGRectMake(aV.frame.origin.x-145+xDelta, aV.frame.origin.y - 150.0, aV.frame.size.width, aV.frame.size.height);
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.7];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[aV setFrame:endFrame];
[UIView commitAnimations];
}
}
-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view didChangeDragState:(MKAnnotationViewDragState)newState fromOldState:(MKAnnotationViewDragState)oldState {
if (oldState == MKAnnotationViewDragStateDragging) {
addAnnotation *annotation = (addAnnotation *)view.annotation;
annotation.subtitle = [NSString stringWithFormat:#"%f %f", annotation.coordinate.latitude, annotation.coordinate.longitude];
}
if (newState == MKAnnotationViewDragStateEnding) {
CLLocationCoordinate2D droppedAt = view.annotation.coordinate;
NSLog(#"dropped at %f,%f", droppedAt.latitude, droppedAt.longitude);
}
}
The problem is that didChangeDragState is never being called (I've set a breakpoint in the routine to be sure). Everything else is working fine. My icons animate into the view, etc. When I tap the icon and hold my finger down the icon stays in place (the map doesn't move either which makes me think that I've actually hit the icon). Am I missing some kind of initialization?
Got it! The problem was in the interface file of my custom annotation class. The offending line read:
#property (nonatomic,readonly) CLLocationCoordinate2D coordinate;
It has to read:
#property (nonatomic,readwrite,assign) CLLocationCoordinate2D coordinate;
I guess that it has to have read/write capability.
Use MKPinAnnotationView instead of MKAnnotationView.
MKPinAnnotationView *pin = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:#"purple_pin"];
if (pin==nil)
{
pin = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"purple_pin"];} else {
pin.annotation=annotation;
}
pin.pinColor = MKPinAnnotationColorPurple;
pin.draggable = TRUE;
return pin;
This is a tricky one! I've just implemented something similar (though I subclassed MKAnnotationView) and when I tried adding the annotationView:didChange delegate method to my view controller it didn't get called even though I was able to drag the annotation view??
I also copy/pasted your code into my view controller and it worked straight out of the box, with the delegate method being called and all!
The only thing I can think of is that instead of passing mvMap to dequeueReusableAnnotationViewWithIdentifier: try passing the mapView object that is supplied by the delegate method. Based on the code you provided above I am unable to tell if they are the same object so it might be worth a shot?
aView=(MKAnnotationView *) [mvMap dequeueReusableAnnotationViewWithIdentifier:annotation.title];
[EDIT TO ADD MY CODE AS REFERENCE]
- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id<MKAnnotation>)annotation
{
static NSString* ParkAnnotationIdentifier = #"ParkAnnotationIdentifier";
MKAnnotationView* parkAnnotationView = (MKAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:ParkAnnotationIdentifier];
if (!parkAnnotationView)
{
MKAnnotationView *annotationView = [[[MKAnnotationView alloc] initWithAnnotation:annotation
reuseIdentifier:ParkAnnotationIdentifier] autorelease];
UIImage *imageIcon = [UIImage imageNamed:#"scooterIcon.png"];
annotationView.image = imageIcon;
annotationView.draggable = YES;
return annotationView;
}
else
{
parkAnnotationView.annotation = annotation;
}
return parkAnnotationView;
}
if you are using a subview of MKPinAnnotationView and are overriding setSelected(), make sure you call super.setSelected()