I'm working on a Mac OSX application, and I'm having trouble with the callout appearing next to a pin when a pin is clicked on. I do use the MKMapViewDelegate, and override the following method like so:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
if ([annotation isKindOfClass:[LocationPin class]]) {
LocationPin *returnPin = [LocationPin createLocationPinForMapView:self.mapOfPins annotation:annotation];
NSButton *rightButton = [[NSButton alloc] initWithFrame:NSMakeRect(0.0, 0.0, 100.0, 80.0)];
[rightButton setTitle:#"Info"];
[rightButton setBezelStyle:NSShadowlessSquareBezelStyle];
returnPin.rightCalloutAccessoryView = rightButton;
returnPin.canShowCallout = true;
returnPin.annotation = annotation;
return returnPin;
}
My pins are appearing on the map just fine, but the callouts just are not appearing. I know a common problem people have is they forget to set the title, but my pins definitely have titles, as I can NSLog them. I also tried to override the :
-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
method, but it's not getting called when I click on a pin. Any help is appreciated.
Firstly, why aren't you using dequeueReusableAnnotationWithIdentifier: method?
This uses less memory as it reuses the same MKAnnotationView instance instead of allocating a new one each time this method is called. I assume LocationPin is a MKAnnotationView subclass. Also, I think your problem comes from creating an annotation view for another map :self.mapOfPins instead of calling it for the mapView object sent to the delegate.
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[LocationPin class]]) {
static NSString *identifier = #"YourPinIdentifier";
LocationPin *returnPin = (LocationPin *)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (!returnPin){
returnPin = [LocationPin createLocationPinForMapView:mapView annotation:annotation];
}
NSButton *rightButton = [[NSButton alloc] initWithFrame:NSMakeRect(0.0, 0.0, 100.0, 80.0)];
[rightButton setTitle:#"Info"];
[rightButton setBezelStyle:NSShadowlessSquareBezelStyle];
returnPin.rightCalloutAccessoryView = rightButton;
returnPin.canShowCallout = true;
returnPin.annotation = annotation;
return returnPin;
}
return nil;
}
Related
I have an MKMapView, that I drop a number of pins on from an array. Each map pin has an MKAnnotationView that links to a segue and another controller.
All of this is working perfectly now, but I can't see how to pass a variable through the MKAnnotationView to the segue.
I can work with either passing a string or an integer, as I could pass a username as a string, or pass the id of the array and use that in the second view controller after the segue.
My working code for looping through my array, dropping the pins, adding the annotations, and then calling the segue is as below.
This all works fine, populates the map, drops the bins, and the annotations work and link to the new segue. This is all perfect!
I just need to be able to pass a variable through from the map pin array to the second view controller segue.
- (void)loadMapPins {
for (int i=0; i<maparray.count; i++){
Location *item = _feedItems1[i];
CLLocationCoordinate2D myCoordinate = {[item.latitude doubleValue], [item.longitude doubleValue]};
MKPointAnnotation *point = [[MKPointAnnotation alloc] init];
point.coordinate = myCoordinate;
point.title = item.firstname;
point.subtitle = [NSString stringWithFormat: #"%#", item.username];
[self.mapView addAnnotation:point];
}
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation {
if (annotation == self.mapView.userLocation) return nil;
static NSString* AnnotationIdentifier = #"AnnotationIdentifier";
MKPinAnnotationView* customPin = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:AnnotationIdentifier];
customPin.pinColor = MKPinAnnotationColorRed;
customPin.animatesDrop = YES;
customPin.canShowCallout = YES;
UIButton* rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[rightButton addTarget:self action:#selector(showDetails:) forControlEvents:UIControlEventTouchUpInside];
customPin.rightCalloutAccessoryView = rightButton;
return customPin;
}
- (IBAction)showDetails:(id)sender {
[self performSegueWithIdentifier:#"showProfileFromMap" sender:self];
}
You should use this MKMapViewDelegate method
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
CustomAnnotationClass *annotation = view.annotation;
selectedAnnotationVariable = annotation.yourProperty;
[self performSegueWithIdentifier:#"performSegue" sender:self];
}
-(MKAnnotationView *)GUmap:(MKMapView *)GUmap viewForAnnotation:(id <MKAnnotation>)annotation
{
MKPinAnnotationView *pinView =(MKPinAnnotationView *) [GUmap dequeueReusableAnnotationViewWithIdentifier:#"pinView"];
if (pinView==nil)
{
pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"pinView"];
pinView.pinColor= MKPinAnnotationColorRed;
pinView.canShowCallout = YES;
pinView.animatesDrop = YES;
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
pinView.rightCalloutAccessoryView = rightButton;
}
else
{
pinView.annotation = annotation;
}
return pinView;
}
This should animate my pin but it's doing nothing i'll be thankful if you solve my problem.
The delegate method is named incorrectly as GUmap:viewForAnnotation: so the map view isn't calling it and is creating a default pin which does not animate the drop.
Change the method declaration to:
-(MKAnnotationView *)mapView:(MKMapView *)GUmap viewForAnnotation:(id <MKAnnotation>)annotation
^^^^^^^
It's OK to use a different name for the internal parameter name.
The code inside the method is fine the way it is.
You only need to reset properties that are different for each annotation (the re-used view will have the properties set to the values that were applied after first creation).
It doesn't give me any errors or warnings as well. I don't know what other relevant info or details I can provide. Please tell me if it's not enough.
_mapView.delegate is set to self
Method in which the calloutAccessoryControl is set:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
NSLog(#"Enter viewForAnnotation delegate");
static NSString *identifier = #"MyLocation";
if ([annotation isKindOfClass:[MapViewAnnotation class]]) {
MKPinAnnotationView *annotationView = (MKPinAnnotationView *) [_mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView == nil) {
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
} else {
annotationView.annotation = annotation;
}
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
UIImageView *callOutButtonImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"arrow.png"]];
annotationView.rightCalloutAccessoryView = callOutButtonImage;
annotationView.image=[UIImage imageNamed:#"green-node.png"];
return annotationView;
}
return nil;
}
calloutAccessoryControlTabbed:
-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control{
NSLog(#"Control Tabbed!");
_scrollView.hidden = false;
}
The following is from the mapView:annotationView:calloutAccessoryControlTapped: discussion in the MKMapViewDelegate protocol reference document:
Accessory views contain custom content and are positioned on either side of the annotation title text. If a view you specify is a descendant of the UIControl class, the map view calls this method as a convenience whenever the user taps your view. You can use this method to respond to taps and perform any actions associated with that control. For example, if your control displayed additional information about the annotation, you could use this method to present a modal panel with that information.
I can see that you've added a UIImage to your annotation view's right callout accessory view. A UIImage object does not inherit from UIControl. A UIButton object would work.
I have some problems with adding annotations when the user touch the map.
I'm using UIGestureRecognizer to detect the user's touch.
When a long press is detected, I'm calling this function :
- (void)handleLongPressGesture:(UIGestureRecognizer*)gestureRecognizer{
if (gestureRecognizer.state == UIGestureRecognizerStateEnded) return;
NSLog(#"long press");
CGPoint touchPoint = [gestureRecognizer locationInView:mapView];
CLLocationCoordinate2D touchMapCoordinate =
[mapView convertPoint:touchPoint toCoordinateFromView:mapView];
RdvAnnotation *rdvAnnotation = [[RdvAnnotation alloc] init];
[rdvAnnotation initWithCoordinate:touchMapCoordinate];
[mapView removeAnnotations:mapView.annotations];
[mapView addAnnotation:rdvAnnotation];
[rdvAnnotation release]; }
I cans see the NSLog in the console and the rdvAnnotation is initialized with the good coordinate.
I don't understand why I can't see my annotation on the map.
Here's my - (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id <MKAnnotation>)annotation method:
- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id <MKAnnotation>)annotation{
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
if ([annotation isKindOfClass:[RdvAnnotation class]])
{
static NSString* RdvAnnotationIdentifier = #"rdvAnnotationIdentifier";
MKPinAnnotationView* pinView = (MKPinAnnotationView *)
[mapView dequeueReusableAnnotationViewWithIdentifier:RdvAnnotationIdentifier];
if (!pinView)
{
MKPinAnnotationView* customPinView = [[[MKPinAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:RdvAnnotationIdentifier] autorelease];
customPinView.pinColor = MKPinAnnotationColorPurple;
customPinView.animatesDrop = YES;
customPinView.canShowCallout = YES;
return customPinView;
}
else
{
pinView.annotation = annotation;
}
return pinView;
}
return nil;}
My viewDidLoad method:
- (void)viewDidLoad {
[super viewDidLoad];
mapView = [[MKMapView alloc] initWithFrame:self.view.frame];
[mapView setShowsUserLocation:TRUE];
[mapView setMapType:MKMapTypeStandard];
[mapView setDelegate:self];
CLLocationManager *locationManager=[[CLLocationManager alloc] init];
[locationManager setDelegate:self];
[locationManager setDesiredAccuracy:kCLLocationAccuracyNearestTenMeters];
[locationManager startUpdatingLocation];
self.navigationItem.title = #"Rendez-vous" ;
}
I just noticed something that seems weird :
RdvAnnotation *rdvAnnotation = [[RdvAnnotation alloc] init];
[rdvAnnotation initWithCoordinate:touchMapCoordinate];
You're calling init twice on the annotation object. You should just do it this way :
RdvAnnotation *rdvAnnotation = [[RdvAnnotation alloc] initWithCoordinate:touchMapCoordinate]];
EDIT :
If you have code in your init method that you don't want to lose, keep the init and change the value of the coordinate property :
RdvAnnotation *rdvAnnotation = [[RdvAnnotation alloc] init];
rdvAnnotation.coordinate = touchMapCoordinate;
In viewDidLoad you are creating a new map view object.
First, this new map view object is not being added as a subview to self.view so it exists in memory but is invisible.
Second, you probably already have a map view object placed in the view in Interface Builder so you don't need to create a new one.
So what is probably happening is that when you add the annotation, it is being added to the map view object in memory but not the one that is visible (the one that was created in IB).
Instead of creating a new map view in viewDidLoad, connect the mapView IBOutlet to the map view in Interface Builder.
I am working with MKPinAnnotationView annotations pins over a MKMapView. Into the view that I am developing, there is a map centered on a pin. When I call the view the pin drops down and if I make a click over it, it shows the annotation information (canShowCallout = YES). How I can get this callout just when I open the view? Without making a click over the annotation pin.
Thanks for reading.
Edited:
I am using this code.
- (void)viewDidLoad {
[super viewDidLoad];
AddressAnnotation *annotation = [[[AddressAnnotation alloc] initWithCoordinate:self.currentAnnotationCoordinate] autorelease];
[self.mapView addAnnotation:annotation];
[self zoomCoordinate:self.currentAnnotationCoordinate];
}
- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id <MKAnnotation>)annotation {
// If it's the user location, just return nil.
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
else { // Handles the other annotations.
// Try to dequeue an existing pin view first.
static NSString *AnnotationIdentifier = #"AnnotationIdentifier";
MKPinAnnotationView *pinView = (MKPinAnnotationView *)[self.mapView dequeueReusableAnnotationViewWithIdentifier:AnnotationIdentifier];
if (!pinView) {
// If an existing pin view was not available, creates one.
MKPinAnnotationView *customPinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:AnnotationIdentifier] autorelease];
customPinView.animatesDrop = YES;
customPinView.canShowCallout = YES;
// Adds a detail disclosure button.
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[rightButton addTarget:self action:#selector(showDetails:) forControlEvents:UIControlEventTouchUpInside];
customPinView.rightCalloutAccessoryView = rightButton;
return customPinView;
} else
pinView.annotation = annotation;
}
return nil;
}
- (void)mapViewDidFinishLoadingMap:(MKMapView *)theMapView {
AddressAnnotation *annotation = [[[AddressAnnotation alloc] initWithCoordinate:self.currentAnnotationCoordinate] autorelease];
for (id<MKAnnotation> currentAnnotation in mapView.annotations) {
if ([currentAnnotation isEqual:annotation]) {
[mapView selectAnnotation:currentAnnotation animated:FALSE];
}
}
}
Edited:
Calling didAddAnnotationViews: instead of mapViewDidFinishLoadingMap: (as aBitObvious has commented), it works fine.
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views {
id<MKAnnotation> myAnnotation = [self.mapView.annotations objectAtIndex:0];
[self.mapView selectAnnotation:myAnnotation animated:YES];
}
Possible duplicate: How to trigger MKAnnotationView's callout view without touching the pin?
You want to call [mapView selectAnnotation:].