MKAnnotation not getting selected in iOS5 - ios

My app places a pushpin on the map and then selects its using animation so the user has a visual clue and can immediately read the title/subtitle. The following code works in both iOS4 and iOS5, but in iOS5, the annotation doesn't get selected automatically unless I change the animation to NO in the selectAnnotation method.
Any ideas why?
MapAnnotations *pushpin = [[MapAnnotations alloc] initWithCoordinate:coordinate];
pushpin.title = [selectedStation valueForKey:#"name"];
pushpin.subtitle = [selectedStation valueForKey:#"address"];
[stationMap addAnnotation:pushpin];
[stationMap selectAnnotation:pushpin animated:YES];
[pushpin release]; pushpin = nil;

Not sure why it would work before but the animation probably requires the annotation view to be created and ready which is unlikely immediately after adding the annotation.
What you can do is move the selection to the didAddAnnotationViews delegate method which should work on all iOS versions:
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views
{
for (MKAnnotationView *av in views) {
if ([av.annotation isKindOfClass:[MapAnnotations class]]) {
MapAnnotations *pushpin = (MapAnnotations *)av.annotation;
if (_this_pushpin_is_the_one_to_select) {
[mapView selectAnnotation:av.annotation animated:YES];
break; //or return;
}
}
}
}

Related

Wrong presentation of pins in MapKit (color and annotation) [duplicate]

I am trying to make a map, where I can see my current location, and see what the street is called.
so far, I am able to put a pin on my map, but for some reason, I am not getting the callout.
and I have put a NSLog in my viewForAnnotation method, but it is not being called, so i wasn't able to test it.
can someone help me?
-(void)lat:(float)lat lon:(float)lon
{
CLLocationCoordinate2D location;
location.latitude = lat;
location.longitude = lon;
NSLog(#"Latitude: %f, Longitude: %f",location.latitude, location.longitude);
//One location is obtained.. just zoom to that location
MKCoordinateRegion region;
region.center=location;
//Set Zoom level using Span
MKCoordinateSpan span;
span.latitudeDelta=.005f;
span.longitudeDelta=.005f;
region.span=span;
[map setRegion:region animated:TRUE];
//MKReverseGeocoder *geocoder=[[MKReverseGeocoder alloc] initWithCoordinate:location];
//geocoder.delegate=self;
//[geocoder start];
if (cPlacemark != nil) {
[map removeAnnotation:cPlacemark];
}
cPlacemark=[[CustomPlacemark alloc] initWithCoordinate:location];
cPlacemark.title = mPlacemark.thoroughfare;
cPlacemark.subtitle = mPlacemark.locality;
[map addAnnotation:cPlacemark];
[cPlacemark release];
[mLocationManager stopUpdatingLocation];
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
// try to dequeue an existing pin view first
if ([annotation isKindOfClass:[CustomPlacemark class]]){
MKPinAnnotationView *pinView=(MKPinAnnotationView *)[map dequeueReusableAnnotationViewWithIdentifier:#"customIdentifier"];
if (!pinView)
{
// if an existing pin view was not available, create one
MKPinAnnotationView* cPinAnnoView = [[[MKPinAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:#"customIdentifier"] autorelease];
cPinAnnoView.pinColor = MKPinAnnotationColorPurple;
cPinAnnoView.animatesDrop = YES;
cPinAnnoView.canShowCallout = YES;
// Add button
UIButton *leftButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[leftButton addTarget:self action:#selector(annotationViewClick:) forControlEvents:UIControlEventTouchUpInside];
cPinAnnoView.leftCalloutAccessoryView = leftButton;
} else
{
pinView.annotation = annotation;
}
return pinView;
}
return nil;
}
Right now I have customized my viewForAnnotation to be like this.
But I still can't get a callout from my pin and the pin remains red.
But it should be purple of nothing at all
I had the same problem which was not setting the MapView delegate to the File Owner.
Open your nib
Right click on the MapView
Drag the delegate to the File's Owner
I had the same problem, as you mentioned. The delegate had been set to ViewController, but the viewForAnnotation selector was not being called. After some checks, I realized if you do not call addAnotation in the main thread, mapView would not call viewForAnnotation, so following update resolved my problem:
Before:
[_mMapView addAnnotation:marker];
After:
dispatch_async(dispatch_get_main_queue(), ^{
[_mMapView addAnnotation:marker];
});
In order to get the viewForAnnotation to be called, add mapView.delegate=self; to e.g. the viewDidLoad method:
- (void)viewDidLoad {
[super viewDidLoad];
mapView.delegate=self;
}
Could it be that your annotation has been added outside the current view area of the MKMapView?
For storyboard, Ctl drag the MKMapView to the orange circle on the bottom bar of ViewController, and select delegate.
This will solve the problem.
As vatrif mentioned in the comments, you must set your delegate BEFORE adding annotations to your MKMapView object.
Others have already explained, odds are high you have not connected your mapview delegate to your controller. Its the first thing to check
i have been working in ios 9 Mapview related app and I experienced the same problem.
somehow I solved my problem, in my case im resizing the mapview.
I added delegate after i resize the mapview. it works now perfectly.!
After having set the delegate for the mapview if still the viewforannotation not getting called then this is something which you have missed - set the self.mapView.showsUserLocation to YES, in interface builder you can tick the shows userLocation option in attributes inspector.

didDeselectAnnotationView event firing even when selecting a pin

I have two events, one fires on selection of a pin.
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKPinAnnotationView *)view { // when they click on a pin
if([view.annotation isKindOfClass: [MKUserLocation class]] || pinSelected == TRUE) // if its the users location ignore it or if pin already selected
{
return;
}
pinSelected = true;
view.image = [UIImage imageNamed:#"map_pin_selected.png"]; // change the image to selected
[self HideShowDetailsMenu:[view.annotation.title integerValue] ShowDetails:true];
}
which changes the pin to selected and makes a details menu appear with data relating to the selected pin and another which fires on deselecting a pin
- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKPinAnnotationView *)view { // when they click off a pin
if([view.annotation isKindOfClass: [MKUserLocation class]] || pinSelected == FALSE) // if its the users location ignore it or if pin already selected
return;
NSLog(#"This is running");
if ([view.annotation.subtitle integerValue] == [AppConstants Instance].CurrentStore.storeid)
view.image = [UIImage imageNamed:#"map_pin_starred.png"]; // set the pin to the unselected image
else
view.image = [UIImage imageNamed:#"map_pin_unselected.png"]; // change the pin to unselected
pinSelected = false;
[self HideShowDetailsMenu:[view.annotation.title integerValue] ShowDetails:false]; // pin unselected so hide the details menu
}
which will happen when a user deselects a Pin on the MapView.
However my issue that is occuring is that when I select a pin that has already been selected my program crashes, I am trying to circumvent this by implementing a bool that is set to true if a pin is set, which will just 'return' and do nothing if the event is triggered again.
However my issue is that the 'didDeselectAnnotationView' event will trigger when I select a pin that has been selected, even though I am not deselecting a pin at all. This triggers the 'pinSelected' variable to be set to false, then the 'didSelectAnnotationView' even is triggered which with the 'pinSelected' variable set to false will then run again crashing the program.
Have I done something wrong? What can I do in this situation?
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)aView
{
indexPathTag=aView.tag;
[mapView deselectAnnotation:aView.annotation animated:YES];
}
- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)aView
{
}
When didSelectAnnotationView fires, it actually tags the annotation as selected somehow. Then when you click it again, the delegate function doesn't fire because it is 'already selected'. You have to manually deselect the annotation by calling the following function once you're done doing what you want.
So you have to Add [mapView deselectAnnotation:aView.annotation animated:YES]; in didSelectAnnotationView delegate method.

Getting coordinates from pin's annotation's disclosure, on tap

i'll try to keep this very simple :
I'm very new to Objective-C (or programming actually) so i'll probably need an explanation more than just an answer.
What i'm trying to do and can't do (yet):
Getting a pin's coordinate after tapping it's disclosure, and eventually pass it through a segue.
Practically, i'm clicking my "info" icon on a pin I just created, and i'd like to see it's info in another page (a webview). This page would show coordinates (and other stuff that we don't need here).
Why :
Practically, i'm clicking my "info" icon on a pin's annotation created earlier by the user, and i'd like to see it's info in another page (a webview). This page would show coordinates (and other stuff that we don't need here).
My pins coordinates are stored into a
CLLocationCoordinate2D location;
I can't re-use the variables i used before because they might be outdated (it would only work if the user asks for the last one created...)
And I do not know how i can get a pin's coordinates ; there must be a method or something but i jsut can't find it. I've found MANY answers on the internet, and none seemed to work, probably because i didn't understand them properly. Anyway, i couldn't use them.
I'll show you bits of my code, it's pretty straight forward i guess :
There is the viewDidLoad, you probably want to see it, i guess :
- (void)viewDidLoad
{
[super viewDidLoad];
name = [[NSString alloc]init];
detail = [[NSString alloc]init];
UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longpressToGetLocation:)];
lpgr.minimumPressDuration = 2.0; //user must press for 2 seconds
[_map addGestureRecognizer:lpgr];
CLLocationCoordinate2D loc = CLLocationCoordinate2DMake(50.837863, 4.353616);
MKCoordinateSpan span = MKCoordinateSpanMake(.035, .035);
MKCoordinateRegion reg = MKCoordinateRegionMake(loc, span);
self.map.region = reg;
[self buildBaseAnno];
self.map.showsUserLocation = true;
}
I know this is important but to be honest i don't fully understand how this bit works ; still, it does work. :D
-(MKAnnotationView*)mapView:(MKMapView*)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
MKAnnotationView* v = nil;
{
static NSString* ident = #"Pin";
v = [_map dequeueReusableAnnotationViewWithIdentifier:ident];
if (v == nil)
{
v = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:ident];
((MKPinAnnotationView*)v).pinColor = MKPinAnnotationColorRed;
UIButton *infoButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
v.rightCalloutAccessoryView = infoButton;
v.centerOffset= CGPointMake(0,-20);
v.canShowCallout= YES;
}
v.annotation = annotation;
}
return v;
}
Touch pin creation :
- (void)longpressToGetLocation:(UIGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state != UIGestureRecognizerStateBegan)
return;
CGPoint touchPoint = [gestureRecognizer locationInView:self.map];
location = [self.map convertPoint:touchPoint toCoordinateFromView:self.map];
[self showAlertName];
}
The actual annotation/pin creation method
-(void)buildAnno
{
CLLocationCoordinate2D coordinate;
coordinate.latitude = location.latitude;
coordinate.longitude = location.longitude;
MKPointAnnotation* newann = [MKPointAnnotation new];
newann.coordinate = coordinate;
newann.title = name;
newann.subtitle = detail;
[self.map addAnnotation:newann];
}
Please, do tell if you need more of the code ; i'm not really sure what i can give you, as my code is probably 'correct' (or decent), i just need to actually know how to get the information i need (that is, coordinates and other stuff from the pin i just tapped.)
And i actually don't tap the pin, i tap the disclosure in the pin's annotation. Anyway, That's enough for now !
Once i can catch these coordinates, i believe i'll be able to pass them through the segue as passing Data is already well explained here, but if there is anything 'special', i'd be really glad if you could add it to your explanation because i'm still really uncomfortable with all this and most of the tutorials/guides/links i've found didn't really help me.
Thank you very much for your time and help :)
(Edit : I had found a similar question here but i believe i need extra help/explanation.)
You don't need the gesture recogniser - the map view delegate protocol already has a method that tells you when the callout accessory was tapped calloutAccessoryControlTapped and this method receives the relevant annotationView. The annotationView.annotation property gets you back to the relevant annotation object and then you can access its coordinate property to get the coordinates of the pin.
First, create a new property in your class:
#property CLLocationCoordinate2D tappedCoord;
then implement the delegate method
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
MKPointAnnotation *annotation=(MKPointAnnotation*)view.annotation;
self.tappedcoord=annotation.coordinate;
[self performSegueWithIdentifier:#"detailViewSegue"]; // Use your appropriate segue identifier
}
Then you can access the property in prepareForSegue (again, change to the appropriate segue name and destination view controller class)
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"detailViewSegue" ]){
DetailViewController *dvc=(DetailViewController *)segue.destinationViewController;
dvc.coord=self.tappedCoord;
}
Also, since your MapView is displaying the user's location, there will be an annotation for that. You need to address this in your viewForAnnotation method, returning nil if the annotation isn't one of yours. You can check the class of the annotation to determine this -
-(MKAnnotationView*)mapView:(MKMapView*)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
MKAnnotationView* v = nil;
if ([annotation isMemberOfClass:[MKPointAnnotation class]]) {
static NSString* ident = #"Pin";
v = [_map dequeueReusableAnnotationViewWithIdentifier:ident];
if (v == nil)
{
v = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:ident];
((MKPinAnnotationView*)v).pinColor = MKPinAnnotationColorRed;
UIButton *infoButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
v.rightCalloutAccessoryView = infoButton;
v.centerOffset= CGPointMake(0,-20);
v.canShowCallout= YES;
}
v.annotation = annotation;
}
return v;
}

iOS MKMapView starts zoomed out default map but zooms in only on refresh

Everytime I start the app, the first time seeing the map results in a default map that is always zoomed out with no annotations. When I go back on the navigation controller and go back into the map, it now shows the correct region with the appropriate pins. The code I use to add the
- (void) zoomIn {
mapView.showsUserLocation = YES;
CLLocationCoordinate2D annotation;
annotation.latitude = 47.640071;
annotation.longitude = -122.129598;
MKPointAnnotation *annoPoint = [[MKPointAnnotation alloc] init];
annoPoint.coordinate = annotation;
annoPoint.title = #"name";
[mapView addAnnotation:annoPoint];
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(annotation, 500, 500);
[mapView setRegion:region animated:YES];
}
I call this block of code from the viewDidLoad, but it only works after I go back to the main page from the navigation controller and enter this UIViewController again.
Does anyone know what the problem is or have seen it before?
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
// this delegate fonction is called when the userlocation is updated
// try to move your code here
}
you have also
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
}
hope this helps
Show us your viewDidLoad function, you're probably calling zoomIn too early, maybe before your MKMapView has been initialized.

Draggable annotation with iOS : callout info update

I'm having a problem with the callout info of a draggable annotation : I use an MKReverseGeocoder to get the address of the coordinates corresponding to the annotation position.
But there is something I don't manage to control : every time the draggable annotation is dropped, the callout info shows up. Very often, the MKReverseGeocoder did not have time to update the address info before this happens. So in those usual cases, the annotation callout shows the address info corresponding to the previous coordinates of the annotation.
Here is the code :
1)Delegate method called when annotation is dropped :
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)annotationView didChangeDragState:(MKAnnotationViewDragState)newState fromOldState:(MKAnnotationViewDragState)oldState{
if (oldState == MKAnnotationViewDragStateDragging) {
MKReverseGeocoder *reverseGeocoder = [[MKReverseGeocoder alloc] initWithCoordinate:annotationView.annotation.coordinate];
reverseGeocoder.delegate = self;
[reverseGeocoder start];
while (reverseGeocoder.querying) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
[reverseGeocoder release];
}
}
2)Delegate method called when reverseGeocoder finds an answer :
- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFindPlacemark:(MKPlacemark *)placemark {
NSString *newSubtitle = [NSString stringWithFormat:#"%# %#, %# %#, %#",placemark.subThoroughfare,placemark.thoroughfare,placemark.postalCode,placemark.locality, placemark.country];
Grocery *draggableGrocery = [myMapView.annotations objectAtIndex:0];
draggableGrocery.address = newSubtitle;
self.addressNewGrocery = newSubtitle;
}
Any idea ?
Thanks !
I was to post the same question, I've been searching for the same issue since yesterday.
UPDATE:
I just figured out a way, and I'm not sure if it is the right one. Knowing that I have a MKAnnotation subclass called selectedAnnotation, I simply deselected, then selected that object after I got the new title in the revereseGeocoder:didFindPlacemark function:
[mapView deselectAnnotation:selectedAnnotation animated:NO];
[mapView selectAnnotation:selectedAnnotation animated:NO];
for your case your code should look like this :
- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFindPlacemark:(MKPlacemark *)placemark {
NSString *newSubtitle = [NSString stringWithFormat:#"%# %#, %# %#, %#",placemark.subThoroughfare,placemark.thoroughfare,placemark.postalCode,placemark.locality, placemark.country];
Grocery *draggableGrocery = [myMapView.annotations objectAtIndex:0];
draggableGrocery.address = newSubtitle;
self.addressNewGrocery = newSubtitle;
// add the following lines [I assumed that draggableGrocery [Grocery class] is a subclass of MKAnnotation]
[mapView deselectAnnotation:draggableGrocery animated:NO];
[mapView selectAnnotation:draggableGrocery animated:NO];
}
Another option is to set annotationView.canShowCallout = NO just before starting the reverse geocoder, and then set the flag back to YES when done.

Resources