1

I have an app that uses Apple Maps quite successfully: I have a list with some points of interest and if I tap one of those first the application shows it on the map, then, if I tap it, it opens Apple Maps Application to navigate.

The problem is that at the very first tap/start the app opens Apple Maps but neither if it asks the permission to use user's location nor if it doesn't, Apple Maps doesn't work and returns : "Directions Not Available. Direction could not be found between these location." but in General Setting the location service for my app is set as ON.

I thought I had to use a kind of timeout because my app takes too long to parse data for navigation (I retrieve data from an xml) but at the second tap everything works fine, even if I run it from the Mac in debugging mode (and it parses xml even the second time)...

I hope I explained well. Any hints?

   -(MKAnnotationView *)mapView:(MKMapView *)mV viewForAnnotation:(id <MKAnnotation>)annotation 
{
    MKPinAnnotationView *pinView = nil; 
    if(annotation != mapView.userLocation) 
    {
    static NSString *defaultPinID = @"com.invasivecode.pin";
    pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
    if ( pinView == nil ) pinView = [[[MKPinAnnotationView alloc]
                                      initWithAnnotation:annotation reuseIdentifier:defaultPinID] autorelease];

    pinView.pinColor = MKPinAnnotationColorRed; 


    UIButton *myAccessoryButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 24, 24)];
    [myAccessoryButton setBackgroundColor:[UIColor clearColor]];
    [myAccessoryButton setImage:[UIImage imageNamed:@"flag.png"] forState:UIControlStateNormal];
    pinView.rightCalloutAccessoryView = myAccessoryButton;
    pinView.canShowCallout = YES;
    pinView.animatesDrop = YES;
    [myAccessoryButton release];
} 
else {
    [mapView.userLocation setTitle:@"You're here"];
}
return pinView;
}

-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control{

locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self; 
locationManager.desiredAccuracy = kCLLocationAccuracyBest; 
locationManager.distanceFilter = kCLDistanceFilterNone; 
[locationManager startUpdatingLocation];

CLLocation *location = [locationManager location];

// Configure the new event with information from the location
CLLocationCoordinate2D coordinate = [location coordinate];

NSString *latitude = [NSString stringWithFormat:@"%f", coordinate.latitude]; 
NSString *longitude = [NSString stringWithFormat:@"%f", coordinate.longitude];


CLLocationCoordinate2D start = { [latitude floatValue], [longitude floatValue] };
CLLocationCoordinate2D end = {[posto.latitude floatValue], [posto.longitude floatValue]};

NSString *googleMapsURLString = [NSString stringWithFormat:@"http://maps.google.com/?saddr=%1.6f,%1.6f&daddr=%1.6f,%1.6f", start.latitude, start.longitude, end.latitude, end.longitude];

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:googleMapsURLString]];
}
Miwi
  • 774
  • 4
  • 15
  • 28

1 Answers1

2

In calloutAccessoryControlTapped, you are using [locationManager location] immediately after calling startUpdatingLocation.

The location is usually not available right after starting to look for the current location (and if it is, it might be unreliable or outdated).

When the location is available, the location manager will call its didUpdateToLocation delegate method and that's where you should move all the code that's after startUpdatingLocation.

(Even in that delegate method, it might be a good idea to check if newLocation is nil before using it. It happens sometimes with the map view while it's waiting for the user to grant permission for location services (see this question)).

-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
    locationManager = [[CLLocationManager alloc] init];
    locationManager.delegate = self; 
    locationManager.desiredAccuracy = kCLLocationAccuracyBest; 
    locationManager.distanceFilter = kCLDistanceFilterNone; 
    [locationManager startUpdatingLocation];
}

-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
    if (newLocation == nil)
    {
        NSLog(@"didUpdateToLocation: newLocation is nil");
        return;
    }

    //May want to check newLocation.horizontalAccuracy and 
    //newLocation.timestamp to see if the location is accurate 
    //enough for you.  If not, return and wait for a more 
    //accurate location (which may never come).

    //all the code currently after "startUpdatingLocation" goes here...
    CLLocation *location = [locationManager location];
    ...
}

-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
    NSLog(@"locationManager:didFailWithError: %@", error);
}

You may also want to implement didFailWithError to handle any problem obtaining the location or to at least be aware of it.

Community
  • 1
  • 1
  • I've done the correction you told me but now the app, that asks properly the permission to use user's location, enters a kind of loop: it moves from map to app entering always in didUpdateToLocation methods: did I forget to manage something? – Miwi Mar 30 '12 at 13:33
  • 1
    Yes, I forgot to mention: Before calling openURL, do `[locationManager stopUpdatingLocation];` otherwise, when it comes back to your app, it updates the location again and keeps going back to Maps app. –  Mar 30 '12 at 13:36