Службы определения местоположения не работают в iOS 8

Мое приложение, которое отлично работало на iOS 7, не работает с iOS 8 SDK.

CLLocationManager не возвращает местоположение, и я не вижу свое приложение в меню " Настройки" -> " Службы определения местоположения". Я сделал поиск Google по этому вопросу, но ничего не пришло. Что может быть не так?

26 ответов

Решение

Я решил свою собственную проблему.

Видимо в iOS 8 SDK, requestAlwaysAuthorization (для фонового расположения) или requestWhenInUseAuthorization (местоположение только когда передний план) вызов CLLocationManager требуется перед началом обновления местоположения.

Там также должно быть NSLocationAlwaysUsageDescription или же NSLocationWhenInUseUsageDescription ключ в Info.plist с сообщением, которое будет отображаться в приглашении. Добавление их решило мою проблему.

введите описание изображения здесь

Надеюсь, это поможет кому-то еще.

РЕДАКТИРОВАТЬ: Для получения более подробной информации, посмотрите на: http://nevan.net/2014/09/core-location-manager-changes-in-ios-8/

Я тащил свои волосы с той же проблемой. Xcode дает вам ошибку:

Пытаясь начать MapKit Обновление местоположения без запроса авторизации местоположения. Должен позвонить -[CLLocationManager requestWhenInUseAuthorization] или же -[CLLocationManager requestAlwaysAuthorization] первый.

Но даже если вы реализуете один из вышеперечисленных методов, он не будет запрашивать пользователя, если в info.plist отсутствует запись для NSLocationAlwaysUsageDescription или же NSLocationWhenInUseUsageDescription,

Добавьте следующие строки в ваш info.plist, где строковые значения представляют причину, по которой вам нужно получить доступ к местоположению пользователя.

<key>NSLocationWhenInUseUsageDescription</key>
<string>This application requires location services to work</string>

<key>NSLocationAlwaysUsageDescription</key>
<string>This application requires location services to work</string>

Я думаю, что эти записи могли отсутствовать с тех пор, как я запустил этот проект в Xcode 5. Я предполагаю, что Xcode 6 может добавить записи по умолчанию для этих ключей, но не подтвердил.

Вы можете найти больше информации об этих двух настройках здесь

Чтобы убедиться, что это обратно совместимо с iOS 7, вы должны проверить, работает ли пользователь под управлением iOS 8 или iOS 7. Например:

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

//In ViewDidLoad
if(IS_OS_8_OR_LATER) {
   [self.locationManager requestAlwaysAuthorization];
}

[self.locationManager startUpdatingLocation];
- (void)startLocationManager
{
    locationManager = [[CLLocationManager alloc] init];
    locationManager.delegate = self;
    locationManager.distanceFilter = kCLDistanceFilterNone; //whenever we move
    locationManager.desiredAccuracy = kCLLocationAccuracyBest;

    [locationManager startUpdatingLocation];
    [locationManager requestWhenInUseAuthorization]; // Add This Line


}

И в ваш файл info.plist

Согласно документации Apple:

Начиная с iOS 8, наличие NSLocationWhenInUseUsageDescription или NSLocationAlwaysUsageDescription значение ключа в файле Info.plist вашего приложения является обязательным. Затем также необходимо запросить разрешение у пользователя до регистрации обновлений местоположения, либо позвонив [self.myLocationManager requestWhenInUseAuthorization] или же [self.myLocationManager requestAlwaysAuthorization] в зависимости от ваших потребностей. Строка, введенная вами в Info.plist, будет отображена в следующем диалоговом окне.

Если пользователь дает разрешение, это обычное дело. Если они отказывают в разрешении, то делегат не информируется об обновлениях местоположения.

- (void)viewDidLoad
{

    [super viewDidLoad];
    self.locationManager = [[CLLocationManager alloc] init];

    self.locationManager.delegate = self;
    if([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]){
        NSUInteger code = [CLLocationManager authorizationStatus];
        if (code == kCLAuthorizationStatusNotDetermined && ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)] || [self.locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)])) {
            // choose one request according to your business.
            if([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"]){
                [self.locationManager requestAlwaysAuthorization];
            } else if([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"]) {
                [self.locationManager  requestWhenInUseAuthorization];
            } else {
                NSLog(@"Info.plist does not contain NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription");
            }
        }
    }
    [self.locationManager startUpdatingLocation];
}

>  #pragma mark - CLLocationManagerDelegate

    - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
    {
        NSLog(@"didFailWithError: %@", error);
        UIAlertView *errorAlert = [[UIAlertView alloc]
                                   initWithTitle:@"Error" message:@"Failed to Get Your Location" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [errorAlert show];
    }

    - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
    {
        NSLog(@"didUpdateToLocation: %@", newLocation);
        CLLocation *currentLocation = newLocation;

        if (currentLocation != nil) {
            longitudeLabel.text = [NSString stringWithFormat:@"%.8f", currentLocation.coordinate.longitude];
            latitudeLabel.text = [NSString stringWithFormat:@"%.8f", currentLocation.coordinate.latitude];
        }
    }

В iOS 8 вам нужно сделать две дополнительные вещи, чтобы заставить местоположение работать: добавьте ключ в ваш Info.plist и запросите авторизацию у менеджера местоположений, попросив его запустить. Существует два ключа Info.plist для авторизации нового местоположения. Требуется один или оба этих ключа. Если ни один из ключей не существует, вы можете вызвать startUpdatingLocation, но менеджер местоположения фактически не запустится. Он также не отправит сообщение об ошибке делегату (так как он никогда не запускался, он не может потерпеть неудачу). Также произойдет сбой, если вы добавите один или оба ключа, но забудете явно запросить авторизацию. Поэтому первое, что вам нужно сделать, это добавить один или оба из следующих ключей в ваш файл Info.plist:

  • NSLocationWhenInUseUsageDescription
  • NSLocationAlwaysUsageDescription

Оба эти ключа принимают строку

это описание того, почему вам нужны услуги определения местоположения. Вы можете ввести строку типа "Местоположение требуется, чтобы узнать, где вы находитесь", которую, как и в iOS 7, можно локализовать в файле InfoPlist.strings.

Мое решение, которое может быть скомпилировано в Xcode 5:

#ifdef __IPHONE_8_0
    NSUInteger code = [CLLocationManager authorizationStatus];
    if (code == kCLAuthorizationStatusNotDetermined && ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)] || [self.locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)])) {
        // choose one request according to your business.
        if([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"]){
            [self.locationManager requestAlwaysAuthorization];
        } else if([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"]) {
            [self.locationManager  requestWhenInUseAuthorization];
        } else {
            NSLog(@"Info.plist does not contain NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription");
        }
    }
#endif
    [self.locationManager startUpdatingLocation];

Старый код для запроса местоположения не будет работать в iOS 8. Вы можете попробовать этот метод для авторизации местоположения:

- (void)requestAlwaysAuthorization
{
    CLAuthorizationStatus status = [CLLocationManager authorizationStatus];

    // If the status is denied or only granted for when in use, display an alert
    if (status == kCLAuthorizationStatusAuthorizedWhenInUse || status ==        kCLAuthorizationStatusDenied) {
        NSString *title;
        title = (status == kCLAuthorizationStatusDenied) ? @"Location services are off" :   @"Background location is not enabled";
        NSString *message = @"To use background location you must turn on 'Always' in the Location Services Settings";

        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:title
                                                            message:message
                                                           delegate:self
                                                  cancelButtonTitle:@"Cancel"
                                                  otherButtonTitles:@"Settings", nil];
        [alertView show];
    }
    // The user has not enabled any location services. Request background authorization.
    else if (status == kCLAuthorizationStatusNotDetermined) {
        [self.locationManager requestAlwaysAuthorization];
    }
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    if (buttonIndex == 1) {
        // Send the user to the Settings for this app
        NSURL *settingsURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
        [[UIApplication sharedApplication] openURL:settingsURL];
    }
}

В iOS 8 вам нужно сделать две дополнительные вещи, чтобы заставить работать местоположение: добавить ключ в ваш Info.plist и запросить авторизацию у менеджера местоположения, попросив его запустить

info.plist:

<key>NSLocationUsageDescription</key>
<string>I need location</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>I need location</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>I need location</string>

Добавьте это в свой код

if (IS_OS_8_OR_LATER)
{
    [locationmanager requestWhenInUseAuthorization];

    [locationmanager requestAlwaysAuthorization];
}

Одна распространенная ошибка для разработчиков Swift:

Сначала убедитесь, что вы добавили значение в список для любого NSLocationWhenInUseUsageDescription или же NSLocationAlwaysUsageDescription,

Если вы все еще не видите всплывающее окно с запросом авторизации, посмотрите, ставите ли вы строку var locationManager = CLLocationManager() в вашем контроллере представления viewDidLoad метод. Если вы это сделаете, то даже если вы позвоните locationManager.requestWhenInUseAuthorization()ничего не появится. Это связано с тем, что после выполнения viewDidLoad переменная locationManager освобождается (очищается).

Решение состоит в том, чтобы найти линию var locationManager = CLLocationManager() в верхней части метода класса.

Я работал над приложением, которое было обновлено до iOS 8, и службы определения местоположения перестали работать. Вы, вероятно, получите ошибку в области отладки следующим образом:

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

Я сделал наименее навязчивую процедуру. Первое добавление NSLocationAlwaysUsageDescription запись в ваш info.plist:

Обратите внимание, что я не заполнил значение для этого ключа. Это все еще работает, и я не обеспокоен, потому что это приложение в доме. Кроме того, уже есть заголовок с просьбой использовать службы определения местоположения, поэтому я не хотел делать ничего лишнего.

Далее я создал условное для iOS 8:

if ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) {
    [_locationManager requestAlwaysAuthorization];
}

После этого locationManager:didChangeAuthorizationStatus: метод это вызов:

- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:  (CLAuthorizationStatus)status
{
    [self gotoCurrenLocation];
}

И теперь все отлично работает. Как всегда, ознакомьтесь с документацией.

До [locationManager startUpdatingLocation];, добавьте запрос сервисов определения местоположения iOS8:

if([locationManager respondsToSelector:@selector(requestAlwaysAuthorization)])
    [locationManager requestAlwaysAuthorization];

Изменить приложение Info.plist и добавить ключ NSLocationAlwaysUsageDescription со строковым значением, которое будет отображаться пользователю (например, We do our best to preserve your battery life.)

Если ваше приложение нуждается в услугах определения местоположения только при открытом приложении, замените:

requestAlwaysAuthorization с requestWhenInUseAuthorization а также

NSLocationAlwaysUsageDescription с NSLocationWhenInUseUsageDescription,

Решение с обратной совместимостью:

SEL requestSelector = NSSelectorFromString(@"requestWhenInUseAuthorization");
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined &&
    [self.locationManager respondsToSelector:requestSelector]) {
    [self.locationManager performSelector:requestSelector withObject:NULL];
} else {
    [self.locationManager startUpdatingLocation];
}

Настройте ключ NSLocationWhenInUseUsageDescription в вашем Info.plist

Решение с обратной совместимостью, которое не выдает предупреждений Xcode:

SEL requestSelector = NSSelectorFromString(@"requestWhenInUseAuthorization");
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined &&
  [self.locationManager respondsToSelector:requestSelector]) {
((void (*)(id, SEL))[self.locationManager methodForSelector:requestSelector])(self.locationManager, requestSelector);
  [self.locationManager startUpdatingLocation];
} else {
  [self.locationManager startUpdatingLocation];
}

Настроить NSLocationWhenInUseUsageDescription введите ваш Info.plist,

Для iOS версии 11.0+: настройка NSLocationAlwaysAndWhenInUseUsageDescription введите ваш Info.plist, вместе с другими 2 ключами.

Это проблема с ios 8 Добавьте это к своему коду

if (IS_OS_8_OR_LATER)
{
    [locationmanager requestWhenInUseAuthorization];

    [locationmanager requestAlwaysAuthorization];
}

и в info.plist:

 <key>NSLocationUsageDescription</key>
 <string>I need location</string>
 <key>NSLocationAlwaysUsageDescription</key>
 <string>I need location</string>
 <key>NSLocationWhenInUseUsageDescription</key>
 <string>I need location</string>

Objective-C Процедура Следуйте инструкциям ниже:

Для iOS-11 Для iOS 11 посмотрите на этот ответ: доступ к местоположению iOS 11

Необходимо добавить два ключа в лист и предоставить сообщение, как показано на рисунке ниже:

 1. NSLocationAlwaysAndWhenInUseUsageDescription 
 2. NSLocationWhenInUseUsageDescription
 3. NSLocationAlwaysUsageDescription

Для iOS-10 и ниже:

NSLocationWhenInUseUsageDescription

locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
if([locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]){
    [locationManager requestWhenInUseAuthorization];
}else{
    [locationManager startUpdatingLocation];
} 

Методы делегирования

#pragma mark - Lolcation Update 
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
    NSLog(@"didFailWithError: %@", error);
    UIAlertView *errorAlert = [[UIAlertView alloc]
                               initWithTitle:@"Error" message:@"Failed to Get Your Location" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
    [errorAlert show];
}
-(void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
    switch (status) {
        case kCLAuthorizationStatusNotDetermined:
        case kCLAuthorizationStatusRestricted:
        case kCLAuthorizationStatusDenied:
        {
            // do some error handling
        }
            break;
        default:{
            [locationManager startUpdatingLocation];
        }
            break;
    }
}
- (void)locationManager:(CLLocationManager *)manager
     didUpdateLocations:(NSArray *)locations
{
    CLLocation *location = [locations lastObject];
    userLatitude =  [NSString stringWithFormat:@"%f", location.coordinate.latitude] ;
    userLongitude =  [NSString stringWithFormat:@"%f",location.coordinate.longitude];
    [locationManager stopUpdatingLocation];
}

Быстрая процедура

Следуйте инструкциям ниже:

Для iOS-11 Для iOS 11 посмотрите на этот ответ: доступ к местоположению iOS 11

Необходимо добавить два ключа в лист и предоставить сообщение, как показано на рисунке ниже:

 1. NSLocationAlwaysAndWhenInUseUsageDescription 
 2. NSLocationWhenInUseUsageDescription
 3. NSLocationAlwaysUsageDescription

Для iOS-10 и ниже:

import CoreLocation
class ViewController: UIViewController ,CLLocationManagerDelegate {
var locationManager = CLLocationManager()

//MARK- Update Location 
func updateMyLocation(){
    locationManager.delegate = self;
    locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
    if locationManager.respondsToSelector(#selector(CLLocationManager.requestWhenInUseAuthorization)){
       locationManager.requestWhenInUseAuthorization()
    }
    else{
        locationManager.startUpdatingLocation()
    }
}

Методы делегирования

//MARK: Location Update
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
    NSLog("Error to update location :%@",error)
}
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
    switch status {
    case .NotDetermined: break
    case .Restricted: break
    case .Denied:
            NSLog("do some error handling")
        break
    default:
        locationManager.startUpdatingLocation()
    }
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
     let location = locations.last! as CLLocation
    var latitude = location.coordinate.latitude
    var longitude = location.coordinate.longitude

}

Чтобы получить доступ к местоположению пользователя в iOS 8, вам нужно будет добавить

NSLocationAlwaysUsageDescription in the Info.plist 

При этом у пользователя будет запрашиваться разрешение на получение его текущего местоположения.

Для тех, кто использует Xamarin, мне пришлось добавить ключ NSLocationWhenInUseUsageDescription в info.plist вручную, поскольку он не был доступен в раскрывающихся списках ни в Xamarin 5.5.3 Build 6, ни в XCode 6.1 - только NSLocationUsageDescription был в списке, и это вызвало CLLocationManager продолжать молча терпеть неудачу.

Я получаю аналогичную ошибку в iOS9 (работа с Xcode 7 и Swift 2): при попытке запуска обновлений местоположения MapKit без запроса авторизации местоположения. Сначала необходимо вызвать -[CLLocationManager requestWhenInUseAuthorization] или -[CLLocationManager requestAlwaysAuthorization]. Я следовал учебному пособию, но он использовал iOS8 и Swift 1.2. В Xcode 7 и Swift 2 есть некоторые изменения, я нашел этот код, и он отлично работает для меня (если кому-то нужна помощь):

import UIKit
import MapKit
import CoreLocation

class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {

    // MARK: Properties
    @IBOutlet weak var mapView: MKMapView!

    let locationManager = CLLocationManager()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.locationManager.delegate = self
        self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
        self.locationManager.requestWhenInUseAuthorization()
        self.locationManager.startUpdatingLocation()
        self.mapView.showsUserLocation = true

    }

    // MARK: - Location Delegate Methods

    func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        let location = locations.last
        let center = CLLocationCoordinate2D(latitude: location!.coordinate.latitude, longitude: location!.coordinate.longitude)
        let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 1, longitudeDelta: 1))
        self.mapView.setRegion(region, animated: true)
    }

    func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
        print("Errors: " + error.localizedDescription)
    }
}

Наконец, я поместил это в info.plist: список свойств информации: NSLocationWhenInUseUsageDescription Значение: приложению нужны серверы местоположения для персонала

Храните информацию о Cocoa Keys всегда под рукой для этих обновлений, вот ссылка:

https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html

Наслаждаться.

Маленький помощник для всех вас, у которых более одного файла Info.plist...

find . -name Info.plist | xargs -I {} /usr/libexec/PlistBuddy -c 'Add NSLocationWhenInUseUsageDescription string' {} 

Он добавит необходимый тег ко всем файлам Info.plist в текущем каталоге (и подпапках).

Другой это:

find . -name Info.plist | xargs -I {} /usr/libexec/PlistBuddy -c 'Set NSLocationWhenInUseUsageDescription $YOURDESCRIPTION' {} 

Это добавит ваше описание ко всем файлам.

Для того, чтобы получить доступ к местоположению пользователей в iOS. Вам нужно добавить два ключа

NSLocationWhenInUseUsageDescription

NSLocationAlwaysUsageDescription

в файл Info.plist.

    <key>NSLocationWhenInUseUsageDescription</key>
    <string>Because I want to know where you are!</string>
    <key>NSLocationAlwaysUsageDescription</key>
    <string>Want to know where you are!</string>

Смотрите это изображение ниже.

Info.plist image

        // ** Don't forget to add NSLocationWhenInUseUsageDescription in MyApp-Info.plist and give it a string

        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];
        }
        [self.locationManager startUpdatingLocation];


    // Location Manager Delegate Methods    
    - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
    {
        NSLog(@"%@", [locations lastObject]);

}
  1. Добавить ключ NSLocationWhenInUseUsageDescription или же NSLocationAlwaysUsageDescription (использование фона в фоновом режиме) со строкой с просьбой использовать GPS на каждом info.plist от каждой цели.

  2. Запросите разрешение, запустив:

    [self initLocationManager: locationManager];

куда initLocationManager является:

// asks for GPS authorization on iOS 8
-(void) initLocationManager:(CLLocationManager *) locationManager{

    locationManager = [[CLLocationManager alloc]init];

    if([locationManager respondsToSelector:@selector(requestAlwaysAuthorization)])
        [locationManager requestAlwaysAuthorization];
}

Помните, что если ключи не на каждом info.plist для каждой цели приложение не будет спрашивать пользователя. if обеспечивает совместимость с iOS 7 и respondsToSelector: метод гарантирует будущую совместимость, а не просто решение проблемы для iOS 7 и 8.

Я добавляю эти ключи в InfoPlist.strings в iOS 8.4, iPad mini 2. Тоже работает. Я не ставлю какие-либо ключи, как NSLocationWhenInUseUsageDescription, в моем Info.plist,


InfoPlist.strings:

"NSLocationWhenInUseUsageDescription" = "I need GPS information....";

Основываясь на этой теме, он сказал: as in iOS 7, может быть локализован в InfoPlist.strings. В моем тесте эти ключи могут быть настроены непосредственно в файле InfoPlist.strings,

Поэтому первое, что вам нужно сделать, это добавить один или оба> следующих ключа в ваш файл Info.plist:

  • NSLocationWhenInUseUsageDescription
  • NSLocationAlwaysUsageDescription

Оба этих ключа принимают строку, которая является описанием того, зачем вам нужны службы определения местоположения. Вы можете ввести строку типа "Местоположение требуется, чтобы узнать, где вы находитесь", которую, как и в iOS 7, можно локализовать в файле InfoPlist.strings.


ОБНОВИТЬ:

Я думаю @IOS метод лучше. Добавить ключ к Info.plist с пустым значением и добавить локализованные строки в InfoPlist.strings,

Проблема для меня заключалась в том, что класс, который был CLLocationManagerDelegate был закрытым, что препятствовало вызову всех методов делегата. Думаю, это не очень распространенная ситуация, но я подумал, что упомяну это, если кто-нибудь поможет.

Другие вопросы по тегам