I have three pins on a mapview. I'd like to give each one a different color. In the delegate method viewForAnnotation, I'm doing this:
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation{
MKPinAnnotationView *annView=[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"anAddress"];
annView.pinColor = MKPinAnnotationColorGreen;
annView.animatesDrop=TRUE;
annView.canShowCallout = YES;
annView.calloutOffset = CGPointMake(-5, 5);
return annView;
}
I was thinking to create an array of MKPinAnnotationViews but how can I get the correct one since the delegate method isn't indexed to anything?
To distinguish between UIView, you can use the tag property and its corresponding viewWithTag: method.
However, in your context, I would recommend adding the color to your annotation class. Then you can ensure that you don't reuse the same caller for multiple pins.
Related
I am trying to use the CalloutAccessory feature, but the delegate method never gets called. I have the delegate properly set up as other mapview delegate methods in my code are firing fine, but for some reason for this one, the delegate method never gets called when the button is tapped.
I have tried extending RouteViewAnnotation from both the MKAnnotationView and the MKPinAnnotationView, and it makes no difference.. the delegate method never gets called.
What am I missing? Do I need something else that isn't here for this to work? The RouteAnnotationView just overrides the drawrect, and has no other code in it.
Relevant Code:
In ViewForAnnotation
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation{
if([annotation isMemberOfClass:[RouteAnnotation class]])
{
RouteAnnotationView *routeAnnotationView = [[RouteAnnotationView alloc] initWithFrame:(CGRectMake(0,0,100,50))] ;
[routeAnnotationView setBackgroundColor:[UIColor clearColor]];
routeAnnotationView.centerOffset = CGPointMake(0,-25);
routeAnnotationView.canShowCallout = TRUE;
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 100.0, 40.0)];
[button setTitle:#"Select" forState:UIControlStateNormal];
button.backgroundColor = [UIColor blueColor];
[routeAnnotationView setRightCalloutAccessoryView:button];
return routeAnnotationView;
}
.
.
.
}
In calloutAccssoryControlTapped
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
NSLog(#"CALLOUT BUTTON TOUCHED");
}
Okay, I figured it out... I had a tapGestureRecognizer assigned to the MapView (A WildcardGestureRecognizer (https://github.com/OrbJapan/ResizableMKCircleOverlay/blob/master/MapView/WildcardGestureRecognizer.m) to be exact) added to my MapView, and while this had not interfered in any way with annotation touches, or other touches in the MapView or MapView Delegate methods I was using, for some reason this completely interfered with the calloutAccessoryControlTapped Delegate method and it was never called... once I removed this the method called without issue.
i wondering if someone may be able to help me. I am new to xcode and im trying to build a basic app. i have followed this tutorial
http://rshankar.com/how-to-add-annotation-to-mapview-in-ios/
i have copied the source code directly, i don't get any errors or warnings, however when running the app the pin locations do not show. Im not sure if they are there ( just invisible ) or whether they are not showing at all. I can't seem to find the issue.
Would anyone be able to suggest what the problem could be?
Thanks in advance.
actually managed to fix this, turns out the coordinates were round the wrong way!!!! Noob error!! I would like to change the callout on the pins if anyone could shed some light on where to change the code on the tutorial.
Thanks
To change the callout of the pins:
make the class that has your map view implement the <MKMapViewDelegate> protocol
set the delegate of your map view to that class (e.g. mapView.delegate = self;)
implement MKMapViewDelegate's (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation method in that class
something like this:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
// If it's the user location, just return nil
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
// Handle custom annotations
if ([annotation isKindOfClass:[NAME_OF_CLASS_IMPLEMENTING_MKANNOTATION_PROTOCOL class]])
{
// Try to dequeue an existing annotation view first
MKAnnotationView *annotationView = (MKAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:#"AnnotationViewIdentifier"];
if (!annotationView)
{
// If an existing pin view was not available, create one
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"AnnotationViewIdentifier"];
annotationView.canShowCallout = YES;
// set callout
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
annotationView.rightCalloutAccessoryView = rightButton;
}
else
{
annotationView.annotation = annotation;
}
return annotationView;
}
return nil;
}
On your map pins (which should be made from a class that implements the MKAnnotation protocol), you should be able to set the title and subtitle properties.
I have a single annotation on a map view. I can select it programmaticly, but the I tap it nothing happens. Could you help me? Did anyone encounter similar problem? Here is mehod for setting up anotations:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
MKAnnotationView *aView = [mapView dequeueReusableAnnotationViewWithIdentifier:#"MapVC"];
if (!aView) {
aView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"MapVC"];
aView.canShowCallout = YES;
aView.draggable=YES;
aView.leftCalloutAccessoryView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
// could put a rightCalloutAccessoryView here
}
aView.annotation = annotation;
[(UIImageView *)aView.leftCalloutAccessoryView setImage:nil];
return aView;
}
And adding them to map view:
- (void)updateMapView
{
if (self.mapView.annotations) [self.mapView removeAnnotations:self.mapView.annotations];
if (self.annotation) [self.mapView addAnnotation:self.annotation];
}
And mehod reacting to pressing of annotations:
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)aView
{
NSLog(#"did select annotation");
}
By the way, method [self.mapView selectAnnotation:annotation] works, but doesn't put up a callout(i checked it with breakpoint). While just taping annotation doesn't(again cheked through breakpoints).
If an annotation's title is nil or blank, the callout will not show (even if everything else is set properly including canShowCallout).
When you tap on an annotation, the didSelectAnnotationView delegate method will get called and if the annotation has a non-blank title, the callout will be displayed.
Regarding your question in the comments:
...is it right I have a seperate class to wrap all my data in to, my
annotation class contains an instance of that data class?
There's nothing wrong with this.
If you want to keep map-related logic separate from the base class, that's fine and probably a good idea for a complex app where the base data class may be used for more than just annotations.
If your app is very simple and the data is only used for annotations, you could keep things very simple and combine the two but it's not a requirement.
As long as you stick to using direct references instead of trying to, for example, use array indexes or view/button tags to link back to some data object from the annotation, the "right" class implementation depends on what works for your app.
Try setting canShowCallout property of the MKAnnotationView to YES in case you didn't.
I know you can create a custom annotation view using something like:
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
MKPinAnnotationView *annotationView = [[[CustomAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"CustomAnnotation"] autorelease];
annotationView.image = [UIImage imageNamed:#"customPin.png"];
return annotationView;
}
.. but how do I change the image in other parts of my code.. (after it has been created with the above)?
You probably don't need the answer anymore, but still, the question is unanswered. What I usually do is add a property to the annotation, telling which image should be used. It can be a BOOL, a UIImage, or pretty much whatever you like.
In viewForAnnotation, I check for that value and set the appropriate image.
Whenever I want to update the image, I change the property's value, and I remove and add the annotation :
[theMapView removeAnnotation: myAnnotation];
[theMapView addAnnotation: myAnnotation];
That way, the annotation is re-drawn.
I was wondering if anyone knows of any subclasses for the MKAnnotationView class. On apples documentation they say one example is the MKPinAnnotationView so I was wondering if there were other pre-created subclasses like the one used to track the devices current location. If anyone has tips for creating my own subclass of the MKAnnotationView class that be great as well.
Thanks,
bubster
In case anyone is still interested in this:
You can get all the subclasses of a class using the Objective-C runtime functions, as described here: http://cocoawithlove.com/2010/01/getting-subclasses-of-objective-c-class.html
Other classes that inherit from MKAnnotationView are:
MKTransitCalloutView, MKAdAnnotationView, MKUserLocationView, MKUserLocationBreadCrumbView, and MKPinAnnotationView
where MKPinAnnotationView is the only one that is documented. All others are private classes that Apple uses internally.
I don't know of any other templates, but that does not mean that they don't exist. :)
Anyway, here's how to create custom ones:
Create a new class conforming to the MKAnnotation protocol. You will need to have two instance variables of type NSString* named title and subtitle and one of type CLLocationCoordinate2D named coordinate and an appropriate setter method (e.g. property). Those strings are going to be displayed in the callout. In the delegate of your mapView, implement the method -mapView:viewForAnnotation: in a similar way as you would implement the datasource for a UITableView. That is, dequeueing an annotationView by an identifier, setting the new properties (e.g. a button of type UIButtonTypeDetailDisclosure for the right accessory view). You will want to add an image to display underneath the offset. Just use the image property of your MKAnnotationView. The center of your custom image will be placed at the coordinate specified, so you may want to give an offset: aView.centerOffset = CGPointMake(0, -20)
Here is some sample code:
- (MKAnnotationView *) mapView: (MKMapView *) mapView viewForAnnotation: (id<MKAnnotation>) annotation {
// reuse a view, if one exists
MKAnnotationView *aView = [mapView dequeueReusableAnnotationViewWithIdentifier:#"pinView"];
// create a new view else
if (!aView) {
aView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"pinView"];
}
// now configure the view
aView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[(UIButton*)aView.rightCalloutAccessoryView addTarget:self action:#selector(showDetails:) forControlEvents:UIControlEventTouchUpInside];
aView.canShowCallout = YES;
aView.enabled = YES;
aView.image = [UIImage imageNamed:#"green_pin.png"];
aView.centerOffset = CGPointMake(0, -20);
return aView;
}