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
Related
i have added a detail button on my pin annotation. i want to go to another view. when i click on it, following code doesn't work. i don't know where is the problem.
- (MKAnnotationView *)mapView:(MKMapView *)mV viewForAnnotation:(id <MKAnnotation>)annotation
{
MKPinAnnotationView *pinAnnotation = nil;
if(annotation != mapview.userLocation)
{
static NSString *defaultPinID = #"myPin";
pinAnnotation = (MKPinAnnotationView *)[mapview dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
if ( pinAnnotation == nil )
pinAnnotation = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:defaultPinID] autorelease];
pinAnnotation.canShowCallout = YES;
//instatiate a detail-disclosure button and set it to appear on right side of annotation
UIButton *infoButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
pinAnnotation.rightCalloutAccessoryView = infoButton;
}
return pinAnnotation;
}
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
if ([(UIButton*)control buttonType] == UIButtonTypeDetailDisclosure){
annotationViewController *detailView=[[annotationViewController alloc] initWithNibName:#"annotationViewController" bundle:nil];
[[self navigationController] pushViewController:detailView animated:YES];
[detailView release];
}
}
The method - (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control is a delegate method. That's why you need to set your mapview's delegate to the class that implements this method (here self), in order for it to be called.
You also need to tell the class that you're implementing the MKMapViewDelegate methods. To do so, in you .h, change this line :
#interface MyViewController : UIViewController
To :
#interface MyViewController : UIViewController <MKMapViewDelegate>
Your method should get called now.
I have a view containing a MKMapView, I display sereval pins in it:
- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id <MKAnnotation>)annotation
{
NSLog(#"mapView:viewForAnnotation");
// if it's the user location, just return nil.
if ([annotation isKindOfClass:[MKUserLocation class]]){
return nil;
}
static NSString* AnnotationIdentifier = #"AnnotationIdentifier";
MKPinAnnotationView *pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:AnnotationIdentifier];
pinView.canShowCallout = YES;
pinView.pinColor = MKPinAnnotationColorPurple;
pinView.annotation = annotation;
return pinView;
}
This is working fine but the method:
-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
is not called when the pin is clicked. It seems the click event is not transmitted to the pin but I cannot figure out why. Any idea ?
When the pin itself is clicked, the map view will call the delegate method mapView:didSelectAnnotationView:.
The calloutAccessoryControlTapped method is called when an annotation view's leftCalloutAccessoryView or rightCalloutAccessoryView is tapped. These are controls (usually buttons) that appear on the pin's callout.
It sounds like you are looking for the mapView:didSelectAnnotationView: delegate method instead.
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()
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];