Службы определения местоположения не работают в 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:
- https://developer.apple.com/documentation/corelocation/requesting_permission_to_use_location_services
- https://developer.apple.com/documentation/corelocation/cllocationmanager/1620562-requestwheninuseauthorization
Начиная с 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
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
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 всегда под рукой для этих обновлений, вот ссылка:
Наслаждаться.
Маленький помощник для всех вас, у которых более одного файла 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>
Смотрите это изображение ниже.
// ** 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]);
}
Добавить ключ
NSLocationWhenInUseUsageDescription
или жеNSLocationAlwaysUsageDescription
(использование фона в фоновом режиме) со строкой с просьбой использовать GPS на каждомinfo.plist
от каждой цели.Запросите разрешение, запустив:
[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
был закрытым, что препятствовало вызову всех методов делегата. Думаю, это не очень распространенная ситуация, но я подумал, что упомяну это, если кто-нибудь поможет.