51

I am working with Map Kit in iOS 8 using Obj-C NOT SWIFT. I cannot get the device location it is set a 0.00, 0.00 and I am getting the error:

Trying to start MapKit location updates without prompting for location authorization. Must call -[CLLocationManager requestWhenInUseAuthorization] or -[CLLocationManager requestAlwaysAuthorization] first.

I have implemented: ( I have tried only one at a time and no luck )

if(IS_OS_8_OR_LATER) {
    [self.locationManager requestWhenInUseAuthorization];
    [self.locationManager requestAlwaysAuthorization];
}
[self.locationManager startUpdatingLocation]; 

And in info.plist

NSLocationWhenInUseUsageDescription  :   App would like to use your location.
NSLocationAlwaysUsageDescription  :  App would like to use your location.

I do get prompted to allow the app to use my location but after I agree nothing changes. The location is being showed as 0.00, 0.00.

Code for displaying users location:

//Get Location
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.distanceFilter = kCLDistanceFilterNone;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[self.locationManager startUpdatingLocation];

MKCoordinateRegion region = { { 0.0, 0.0 }, { 0.0, 0.0 } };
region.center.latitude = self.locationManager.location.coordinate.latitude;
region.center.longitude = self.locationManager.location.coordinate.longitude;
region.span.longitudeDelta = 0.005f;
region.span.longitudeDelta = 0.005f;
[mapView setRegion:region animated:YES];

Mike.

**EDIT: View Answer Below.

MBarton
  • 2,087
  • 2
  • 14
  • 17
  • I can't get cl to work right either. with this beta (that feels alpha) I don't think you're to blame if it works on ios7 – Daij-Djan Jul 12 '14 at 21:40
  • For the Nth time: You should not expect locationManager.location to _always_ have valid values immediately after calling startUpdatingLocation. You must implement the didUpdateLocations delegate method and process the location there. Don't forget to set locationManager.delegate to self or the delegate method won't get called. –  Jul 12 '14 at 23:14

8 Answers8

130

I got it working. I've posted my code below to help anyone else having issues.

Here is my full code to get the MapKit Map View working in iOS 8.

In your AppName-Info.plist Add a new row with the key name being:

NSLocationWhenInUseUsageDescription

Or

NSLocationAlwaysUsageDescription

With the value being a string of the message that you want to be displayed:

YourAppName would like to use your location.

In your header file. (I use App Name-Prefix.pch but YourViewController.h will work too)

#define IS_OS_8_OR_LATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)

YourViewController.h

#import <MapKit/MapKit.h>
#import <MapKit/MKAnnotation.h>

@interface YourViewController : UIViewController <MKMapViewDelegate,  CLLocationManagerDelegate> {

}


@property(nonatomic, retain) IBOutlet MKMapView *mapView;
@property(nonatomic, retain) CLLocationManager *locationManager;

YourViewController.m

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.


    mapView.delegate = self;
    self.locationManager = [[CLLocationManager alloc] init];
    self.locationManager.delegate = self;
    #ifdef __IPHONE_8_0
    if(IS_OS_8_OR_LATER) {
         // Use one or the other, not both. Depending on what you put in info.plist
        [self.locationManager requestWhenInUseAuthorization];
        [self.locationManager requestAlwaysAuthorization];
    }
    #endif
    [self.locationManager startUpdatingLocation];

    mapView.showsUserLocation = YES;
    [mapView setMapType:MKMapTypeStandard];
    [mapView setZoomEnabled:YES];
    [mapView setScrollEnabled:YES];
}

-(void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:YES];

    self.locationManager.distanceFilter = kCLDistanceFilterNone;
    self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
    [self.locationManager startUpdatingLocation];
    NSLog(@"%@", [self deviceLocation]);

    //View Area
    MKCoordinateRegion region = { { 0.0, 0.0 }, { 0.0, 0.0 } };
    region.center.latitude = self.locationManager.location.coordinate.latitude;
    region.center.longitude = self.locationManager.location.coordinate.longitude;
    region.span.longitudeDelta = 0.005f;
    region.span.longitudeDelta = 0.005f;
    [mapView setRegion:region animated:YES];

}

- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
    MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(userLocation.coordinate, 800, 800);
    [self.mapView setRegion:[self.mapView regionThatFits:region] animated:YES];
}
- (NSString *)deviceLocation {
    return [NSString stringWithFormat:@"latitude: %f longitude: %f", self.locationManager.location.coordinate.latitude, self.locationManager.location.coordinate.longitude];
}
- (NSString *)deviceLat {
    return [NSString stringWithFormat:@"%f", self.locationManager.location.coordinate.latitude];
}
- (NSString *)deviceLon {
    return [NSString stringWithFormat:@"%f", self.locationManager.location.coordinate.longitude];
}
- (NSString *)deviceAlt {
    return [NSString stringWithFormat:@"%f", self.locationManager.location.altitude];
}

Enjoy!

--Mike

MBarton
  • 2,087
  • 2
  • 14
  • 17
  • 5
    @MBarton prior ios 8 for me was enough uimapkit.showsUserLocation = YES; and mapkit delegates. Now in ios 8 should I declare CLLocationManager to request sufficient autorization? Or is there a way to force mapkit ask for sufficient permission? – Nik Yekimov Sep 15 '14 at 11:06
  • how did you get to this stuff? – Fede Cugliandolo Sep 18 '14 at 02:26
  • 1
    @nyekimov To my knowledge you need to call CLLocationManager to request authorization. – MBarton Sep 19 '14 at 04:12
  • @FedeCugliandolo How did I get to what stuff? – MBarton Sep 19 '14 at 04:12
  • Although it breaks things this is a great feature, now developers can explain why they need your location. I usually don't grant it because i don't know why is so important to know it, except for obvious apps. NSLocationWhenInUseUsageDescription is very important seems, doesn't work without. – Cristi Băluță Sep 20 '14 at 11:04
  • The above code does not compile in xcode5 & ios7: "No visible @interface for CLLocationManager declares selector 'requestWhenInUseAuthorization' " Must add `#ifdef __IPHONE_8_0` – El Dude Oct 10 '14 at 20:55
  • Change where you have the if(IS_OS_8_OR_LATER){ ... } TO: #ifdef __IPHONE_8_0 if(IS_OS_8_OR_LATER) { // Use one or the other, not both. Depending on what you put in info.plist [self.locationManager requestWhenInUseAuthorization]; [self.locationManager requestAlwaysAuthorization]; } #endif – MBarton Oct 11 '14 at 21:46
  • @MBarton : I am following your steps, still location is 0.00, 0.000. Take a look at [this question](http://stackoverflow.com/questions/26356558/mapkit-giving-wrong-lat-lang-ios-8) – Fahim Parkar Oct 14 '14 at 09:35
  • @MBarton : I don't know what was real problem, but if I put what you have in viewDidLoad, it started working... no sure why... – Fahim Parkar Oct 14 '14 at 13:03
  • @MBarton You set the delegate on locationManager before you initialize it? That does not matter anyway because there is no need to set the delegate on it, and also no need to have your viewController conform to CLLocationManagerDelegate since all you want is to call requestWhenInUseAuthorization or requestAlwaysAuthorization on an instance of CLLocationManager (according to your code). Or am I missing something – pnizzle Oct 16 '14 at 06:23
  • 1
    @nyekimov have you found a way to call requestAlwaysAuthorization on the mapViews locationManager, and not have to create a locationManager instance ourselves? I'm sure there is a way to do this properly for MKMapViews. – pnizzle Oct 16 '14 at 06:26
  • The simulator seems to have a bug that makes it appear that the location permission didn't work, so if after applying the steps in this answer it still doesn't work, try it on a device. – Bek Oct 28 '14 at 23:41
  • I recently face this issue and found great info at http://datacalculation.blogspot.in/2014/11/how-to-fix-cllocationmanager-location.html – iOS Test Nov 02 '14 at 17:45
  • 2
    There is no need for the `IS_IOS_8_OR_LATER` macro. Simply check if the `requestWhenInUseAuthorization` (or `requestAlwaysAuthorization`) method exists. – rmaddy Nov 17 '14 at 18:27
  • @MBarton Its only showing blue animating circle, I need to see the country, state & city name just like google maps. Could u pls help me on that, thanks :) – Eshwar Chaitanya Mar 03 '15 at 15:56
  • @EshwarChaitanya If you could post a stackoverflow question so I can fully understand what you are after, I would certainly help. – MBarton Mar 09 '15 at 01:11
  • @MBarton Thanks a lot, but I solved my problem by setting zoom to user location and map view center coordinate property in didupdateuserlocation method, thanks once again :) – Eshwar Chaitanya Mar 10 '15 at 14:00
  • @MBarton : NSLog(@"%@", [self deviceLocation]); not able you compile can i know what's the use of [self deviceLocation] ? – satishiOS25 Mar 21 '15 at 07:52
  • @satish_881 the [self deviceLocation] returns the location of the device. I log this so I can know wether or not location services is returning a GPS location. For example, if it returns "lat: 0.000000 lng: 0.000000" then I know something isn't right. Have you included the deviceLocation method? – MBarton Mar 21 '15 at 19:25
  • @MBarton : Yes i included , and what's happening is . In that map it's showing current location not the Lan and Longitude i'm hardcoded.! – satishiOS25 Mar 28 '15 at 06:02
  • @satish_881 I'm not following what your problem/question is. If you post a new stackoveroverflow detailing what the issue is, and what you are trying to achieve, I would certainly try and help you. (Just make sure to PM or comment the SO link.) – MBarton Mar 28 '15 at 08:38
  • Downvote !!!! It didn't work for me. I tried this with iOS 8.3 sdk with deployment target 7.0. – Karan Alangat Jul 15 '15 at 07:02
  • As of iOS 11, an instance of `CLLocationManager` is necessary only for `requestWhenInUseAuthorization` or `requestAlwaysAuthorization` and can be deallocated in `[locationManager:didChangeAuthorizationStatus:]`. `startUpdatingLocation` might be superfluous, since `MKMapView` keeps delivering `[mapView:didUpdateUserLocation:]` if you use `showsUserLocation` of `YES`. – Gary Sep 11 '18 at 16:54
8

It's not written anywhere, but if your app starts with MapKit, you will still receive the error message "Trying to start MapKit location updates without prompting for location authorization" even after implementing MBarton's answer. To avoid it, you have to create a new view controller before the MapKit, and implement the location manager delegates there. I called it AuthorizationController.

So, in AuthorizationController.h:

#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>

@interface MCIAuthorizationController : UIViewController <CLLocationManagerDelegate>

@property (strong, nonatomic) CLLocationManager *locationManager;

@end

And in AuthorizationController.m:

- (void)viewDidLoad {
    [super viewDidLoad];

    // Location manager
    self.locationManager = [[CLLocationManager alloc] init];
    self.locationManager.delegate = self;

    // Check for iOS 8. Without this guard the code will crash with "unknown selector" on iOS 7.
    if ([self.locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
        [self.locationManager requestWhenInUseAuthorization];
    }
}

#pragma mark - Location Manager delegates

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
    NSLog(@"didUpdateLocations: %@", [locations lastObject]);
}


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

- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
    if (status == kCLAuthorizationStatusAuthorizedWhenInUse) {
        [self.locationManager startUpdatingLocation];
        [self performSegueWithIdentifier:@"startSegue" sender:self];
    } else if (status == kCLAuthorizationStatusDenied) {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Location services not authorized"
                                                        message:@"This app needs you to authorize locations services to work."
                                                       delegate:nil
                                              cancelButtonTitle:@"OK"
                                              otherButtonTitles:nil];
        [alert show];
    } else
        NSLog(@"Wrong location status");
}
Rodrigo Pinto
  • 2,384
  • 22
  • 23
  • That's a very good idea. Or you check the `[CLLocationManager authorizationStatus]` before setting `mapView.showsUserLocation = YES`. This helps also to get rid of the warning. – Sebastian Wramba Dec 04 '14 at 14:57
  • You don't need that extra controller, the message disappears after asking for auth and adding the plist key. – Ven Apr 20 '15 at 15:30
  • It disappears after the user responds to the request for authorisation. So, on the second time the app is used and afterwards the message does not appear anymore. But on the first time and while the user does not reply, the message keeps appearing. – Rodrigo Pinto Apr 30 '15 at 05:23
  • Checking [CLLocationManager authorizationStatus] before setting mapView.showsUserLocation = YES also is not enough. I believe the message is triggered if you do as little as declare a strong variable of CLLocation type. But I agree that creating a view controller just to avoid this is overkill. Thanks for the contributions Sebastian Wramba and @Ven – Rodrigo Pinto Apr 30 '15 at 05:30
3

Try This One:

 (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {

    if (status == kCLAuthorizationStatusAuthorizedWhenInUse) {
        self.mapView.showsUserLocation = YES;
    }
Himanshu
  • 4,327
  • 16
  • 31
  • 39
brusnikin
  • 31
  • 1
2

Your code looks fine, though you do not need to call requestWhenInUseAuthorization and the other requestAlwaysAuthorization , choose one you need.

Code for displaying locations is just yet allocating locationManager, do not expect to get location data instantly.

you need to wait till delegate method gets called : -(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
, then self.locationManager.location will also be set.

PetrV
  • 1,368
  • 2
  • 15
  • 30
  • You may get some more description at http://datacalculation.blogspot.in/2014/11/how-to-fix-cllocationmanager-location.html – iOS Test Nov 02 '14 at 17:41
2

Further to Mikes answer, I found that using both [self.locationManager requestWhenInUseAuthorization]; and [self.locationManager requestAlwaysAuthorization]; as demonstrated in his code does not work. You should only use ONE.

I assume some further changes were made with a more recent/stable version of the API.

2

I had the same problem but adding these two line in plist file solved my problems

NSLocationWhenInUseUsageDescription

And

NSLocationAlwaysUsageDescription

NOTE : Must provide string description of both these values. You can use any of them in your controller file as below

self.locationManager= [[CLLocationManager alloc] init];
self.locationManager.delegate=self;
[self.locationManager requestAlwaysAuthorization];

You must implement CLLOcationManagerDelegate in your controller to access this functionality

Aadil Keshwani
  • 1,385
  • 1
  • 18
  • 29
0

To extend the accepted answer and if you create a sample project with just the above functionality, then apart from CoreLocation and Mapkit frameworks, you might need to add UIKit, Foundation and CoreGraphics framework manually as well in Xcode 6.

Deepak Thakur
  • 3,453
  • 2
  • 37
  • 65
-2

Actually, I am studying the CS193P Lecture 16, which is about location and map view, and I could not make the location manager work in iOS 8, applying what was in the video. Looking at your answer, I could make it work.

The Info.plist was modified as described in the answers (I use the NSLocationWhenInUseUsageDescription).

In AddPhotoViewController.hn the define was added :

#define IS_OS_8_OR_LATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)

In AddPhotoViewController.m, the following code was added in ViewDidLoad (after self.image):

#ifdef __IPHONE_8_0
if(IS_OS_8_OR_LATER)
{
    [self.locationManager requestWhenInUseAuthorization];
}
#endif

The authorization will be asked only once, the first time you launch the application.

The following was also added to AddPhotoViewController.h because it was not said in Lecture 16 :

@property (nonatomic) NSInteger locationErrorCode;

shouldPerformSegueWithIdentifier was modified to include else if (!self.location) :

else if (![self.titleTextField.text length])
        {
            [self alert:@"Title required"];
            return NO;
        }
        else if (!self.location)
        {
            switch (self.locationErrorCode)
            {
                case kCLErrorLocationUnknown:
                    [self alert:@"Couldn't figure out where this photo was taken (yet)."]; break;
                case kCLErrorDenied:
                    [self alert:@"Location Services disabled under Privacy in Settings application."]; break;
                case kCLErrorNetwork:
                    [self alert:@"Can't figure out where this photo is being taken.  Verify your connection to the network."]; break;
                default:
                    [self alert:@"Cant figure out where this photo is being taken, sorry."]; break;
            }
            return NO;
        }
        else
        { // should check imageURL too to be sure we could write the file
            return YES;
        }

didFailWithError was added :

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
    self.locationErrorCode = error.code;
}
  • In viewDidLoad, the getter of locationManager will be called. This getter does : locationManager alloc init, locationManager.delegate = self, set locationManager.desiredAccuracy to kCLLocationAccuracyBest. – Vincent Cohen Mar 09 '15 at 11:10
  • Do not use the `IS_OS_8_OR_LATER` macro. There are proper ways to get if a method can be used or not. – rmaddy Apr 28 '16 at 17:27