Mon application qui fonctionne correctement sur iOS 7 ne fonctionne pas avec le SDK iOS 8.
CLLocationManager
ne renvoie pas de lieu et je ne vois pas mon application sous Paramètres -> Services de localisation. J'ai fait une recherche Google sur la question, mais rien n'a été fait. Quel pourrait être le problème?
J'ai fini par résoudre mon propre problème.
Apparemment, dans iOS 8 SDK, requestAlwaysAuthorization
(pour l'emplacement de l'arrière-plan) ou requestWhenInUseAuthorization
(emplacement uniquement en avant-plan), un appel sur CLLocationManager
est nécessaire avant de commencer les mises à jour d'emplacement.
Il doit également y avoir NSLocationAlwaysUsageDescription
ou NSLocationWhenInUseUsageDescription
dans la touche Info.plist
avec un message à afficher dans l'invite. L'ajout de ceux-ci a résolu mon problème.
J'espère que ça aide quelqu'un d'autre.
EDIT: Pour plus d'informations, consultez: Core-Location-Manager-Changes-in-ios-8
Je me tirais les cheveux avec le même problème. Xcode vous donne l'erreur:
Essayer de démarrer
MapKit
les mises à jour d'emplacement sans demander l'autorisation d'emplacement. Vous devez d'abord appeler-[CLLocationManager requestWhenInUseAuthorization]
ou-[CLLocationManager requestAlwaysAuthorization]
.
Toutefois, même si vous implémentez l'une des méthodes ci-dessus, aucune invite ne sera invitée à moins que l'entrée info.plist contienne une entrée pour NSLocationAlwaysUsageDescription
ou NSLocationWhenInUseUsageDescription
.
Ajoutez les lignes suivantes à votre info.plist où les valeurs de chaîne représentent la raison pour laquelle vous devez accéder à l'emplacement des utilisateurs
<key>NSLocationWhenInUseUsageDescription</key>
<string>This application requires location services to work</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>This application requires location services to work</string>
Je pense que ces entrées ont peut-être été manquantes depuis que j'ai démarré ce projet dans Xcode 5. J'imagine que Xcode 6 pourrait ajouter des entrées par défaut pour ces clés, mais elles n'ont pas été confirmées.
Vous pouvez trouver plus d'informations sur ces deux paramètres ici
Pour vous assurer qu'il est rétro-compatible avec iOS 7, vous devez vérifier si l'utilisateur exécute iOS 8 ou iOS 7. Par exemple:
#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
}
Et à votre fichier info.plist
Selon les Apple docs:
À partir d'iOS 8, la présence d'une valeur de clé NSLocationWhenInUseUsageDescription
ou NSLocationAlwaysUsageDescription
dans le fichier Info.plist de votre application est requise. Il est ensuite également nécessaire de demander l'autorisation de l'utilisateur avant de s'inscrire pour les mises à jour d'emplacement, en appelant [self.myLocationManager requestWhenInUseAuthorization]
ou [self.myLocationManager requestAlwaysAuthorization]
, selon vos besoins. La chaîne que vous avez entrée dans Info.plist sera alors affichée dans la boîte de dialogue suivante.
Si l'utilisateur accorde la permission, les choses se passent normalement. S'ils refusent l'autorisation, le délégué n'est pas informé des mises à jour d'emplacement.
- (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];
}
}
Dans iOS 8, vous devez effectuer deux opérations supplémentaires pour que votre emplacement fonctionne: ajoutez une clé à votre Info.plist et demandez une autorisation au gestionnaire d’emplacement pour lui demander de démarrer. Il existe deux clés Info.plist pour la nouvelle autorisation d’emplacement. Une ou deux de ces clés est nécessaire. Si aucune des clés n’est présente, vous pouvez appeler startUpdatingLocation mais le gestionnaire de sites ne démarre pas réellement. Il n’enverra pas non plus de message d’échec au délégué (car il n’a jamais démarré, il ne peut pas échouer). Il échouera également si vous ajoutez une ou les deux clés, mais oubliez de demander explicitement une autorisation. La première chose à faire est donc d’ajouter une ou les deux clés suivantes à votre fichier Info.plist:
Ces deux clés prennent une chaîne
ce qui explique pourquoi vous avez besoin de services de localisation. Vous pouvez entrer une chaîne telle que "L'emplacement est requis pour savoir où vous vous trouvez" qui, comme dans iOS 7, peut être localisée dans le fichier InfoPlist.strings.
Ma solution qui peut être compilée dans 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];
L'ancien code de localisation demandé ne fonctionnera pas sous iOS 8. Vous pouvez essayer cette méthode pour l'autorisation de localisation:
- (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];
}
}
Dans iOS 8, vous devez effectuer deux opérations supplémentaires pour que votre emplacement fonctionne: ajoutez une clé à votre Info.plist et demandez une autorisation au responsable de la localisation pour lui demander de démarrer.
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>
Ajouter ceci à votre code
if (IS_OS_8_OR_LATER)
{
[locationmanager requestWhenInUseAuthorization];
[locationmanager requestAlwaysAuthorization];
}
ne erreur commune pour les Swift développeurs:
Commencez par vous assurer que vous ajoutez une valeur à la liste de sélection pour NSLocationWhenInUseUsageDescription
ou NSLocationAlwaysUsageDescription
.
Si toujours ne voyez pas de fenêtre vous demandant l'autorisation, vérifiez si vous mettez la ligne var locationManager = CLLocationManager()
dans votre contrôleur View. viewDidLoad
méthode. Si vous le faites, alors même si vous appelez locationManager.requestWhenInUseAuthorization()
, rien ne s'affichera. En effet, après l’exécution de viewDidLoad, la variable locationManager est désallouée (effacée).
La solution consiste à localiser la ligne var locationManager = CLLocationManager()
en haut de la méthode de classe.
Avant [locationManager startUpdatingLocation];
, ajoutez une demande de services de localisation iOS8:
if([locationManager respondsToSelector:@selector(requestAlwaysAuthorization)])
[locationManager requestAlwaysAuthorization];
Modifiez le Info.plist
de votre application et ajoutez la clé NSLocationAlwaysUsageDescription
avec la valeur de chaîne qui sera affichée à l'utilisateur (par exemple, We do our best to preserve your battery life.
).
Si votre application a besoin de services de localisation uniquement pendant que l'application est ouverte, remplacez:
requestAlwaysAuthorization
avec requestWhenInUseAuthorization
et
NSLocationAlwaysUsageDescription
avec NSLocationWhenInUseUsageDescription
.
Je travaillais sur une application mise à niveau vers iOS 8 et les services de localisation ont cessé de fonctionner. Vous obtiendrez probablement une erreur dans la zone de débogage comme ceci:
Trying to start MapKit location updates without prompting for location authorization. Must call -[CLLocationManager requestWhenInUseAuthorization] or -[CLLocationManager requestAlwaysAuthorization] first.
J'ai fait la procédure la moins intrusive. Commencez par ajouter l’entrée NSLocationAlwaysUsageDescription
à votre info.plist:
Remarquez que je n'ai pas renseigné la valeur de cette clé. Cela fonctionne toujours, et je ne suis pas inquiet parce que c'est une application maison. En outre, il existe déjà un titre demandant à utiliser les services de localisation. Je ne voulais donc rien faire de redondant.
Ensuite, j'ai créé une condition pour iOS 8:
if ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) {
[_locationManager requestAlwaysAuthorization];
}
Après cela, la méthode locationManager:didChangeAuthorizationStatus:
est appelée:
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus: (CLAuthorizationStatus)status
{
[self gotoCurrenLocation];
}
Et maintenant tout fonctionne bien. Comme toujours, consultez le documentation .
Solution avec compatibilité ascendante:
SEL requestSelector = NSSelectorFromString(@"requestWhenInUseAuthorization");
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined &&
[self.locationManager respondsToSelector:requestSelector]) {
[self.locationManager performSelector:requestSelector withObject:NULL];
} else {
[self.locationManager startUpdatingLocation];
}
Configurez la clé NSLocationWhenInUseUsageDescription dans votre Info.plist
Solution avec compatibilité ascendante qui ne produit pas d'avertissements 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];
}
Configurez la touche NSLocationWhenInUseUsageDescription
dans votre Info.plist
.
Pour iOS version 11.0 +: touche Setup NSLocationAlwaysAndWhenInUseUsageDescription
dans votre Info.plist
. avec d'autres 2 clés.
Ceci est un problème avec ios 8 Ajoutez ceci à votre code
if (IS_OS_8_OR_LATER)
{
[locationmanager requestWhenInUseAuthorization];
[locationmanager requestAlwaysAuthorization];
}
et à 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>
Pour accéder à l'emplacement de l'utilisateur dans iOS 8, vous devez ajouter,
NSLocationAlwaysUsageDescription in the Info.plist
Cela demandera à l'utilisateur l'autorisation d'obtenir son emplacement actuel.
Procédure Objective-C Suivez les instructions ci-dessous:
Pour iOS-11 Pour iOS 11, jetez un oeil à cette réponse: accès par emplacement pour iOS 11
Besoin d'ajouter deux clés dans la liste et de fournir un message comme ci-dessous:
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];
}
Méthodes de délégué
#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];
}
Procédure rapide
Suivez les instructions ci-dessous:
Pour iOS-11 Pour iOS 11, jetez un oeil à cette réponse: accès par emplacement pour iOS 11
Besoin d'ajouter deux clés dans la liste et de fournir un message comme ci-dessous:
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()
}
}
Méthodes de délégué
//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
}
Pour ceux qui utilisent Xamarin, je devais ajouter la clé NSLocationWhenInUseUsageDescription
à info.plist manuellement car elle n'était pas disponible dans les menus déroulants de Xamarin 5.5.3 Build 6 ou XCode. 6.1 - seul NSLocationUsageDescription
était dans la liste et cela entraînait l'échec continu de CLLocationManager
en silence.
Je reçois une erreur similaire dans iOS9 (avec Xcode 7 et Swift 2): Essayer de démarrer les mises à jour d'emplacement MapKit sans demander l'autorisation d'emplacement. Doit appeler - [CLLocationManager requestWhenInUseAuthorization] ou - [CLLocationManager]. requestAlwaysAuthorization] first. Je suivais un didacticiel, mais le tuteur utilisait iOS8 et Swift 1.2. Il y a quelques changements dans Xcode 7 et Swift 2, j'ai trouvé ce code et il fonctionne très bien pour moi (si quelqu'un a besoin d'aide):
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)
}
}
Enfin, je mets ça dans info.plist: Liste des propriétés d’information: NSLocationWhenInUseUsageDescription Valeur: L’application nécessite des serveurs de localisation pour le personnel
Gardez des clés Cocoa toujours à portée de main pour ces mises à jour, voici le lien:
Prendre plaisir.
Afin d'accéder à l'emplacement des utilisateurs sur iOS. Vous devez ajouter deux clés
NSLocationWhenInUseUsageDescription
NSLocationAlwaysUsageDescription
dans le fichier 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>
Voir cette image ci-dessous.
Un petit assistant pour tous ceux qui ont plus d’un fichier Info.plist ...
find . -name Info.plist | xargs -I {} /usr/libexec/PlistBuddy -c 'Add NSLocationWhenInUseUsageDescription string' {}
Il ajoutera la balise nécessaire à tous les fichiers Info.plist du répertoire (et des sous-dossiers) en cours.
Un autre est:
find . -name Info.plist | xargs -I {} /usr/libexec/PlistBuddy -c 'Set NSLocationWhenInUseUsageDescription $YOURDESCRIPTION' {}
Il va ajouter votre description à tous les fichiers.
// ** 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]);
}
Ajouter la touche NSLocationWhenInUseUsageDescription
ou NSLocationAlwaysUsageDescription
(utilisation du GPS en arrière-plan) avec une chaîne demandant à utiliser le GPS sur chaque info.plist
de chaque cible.
Demandez la permission en exécutant:
[auto initLocationManager: locationManager];
Où initLocationManager
est:
// asks for GPS authorization on iOS 8
-(void) initLocationManager:(CLLocationManager *) locationManager{
locationManager = [[CLLocationManager alloc]init];
if([locationManager respondsToSelector:@selector(requestAlwaysAuthorization)])
[locationManager requestAlwaysAuthorization];
}
Rappelez-vous que si les clés ne sont pas sur chaque info.plist
pour chaque cible, l'application ne le demandera pas à l'utilisateur. La if
assure la compatibilité avec iOS 7 et la méthode respondsToSelector:
garantit la compatibilité future, plutôt que de simplement résoudre le problème pour iOS 7 et 8.
Le problème pour moi était que la classe qui était la CLLocationManagerDelegate
était privée, ce qui empêchait l'appel de toutes les méthodes de délégation. Je suppose que ce n’est pas une situation très courante, mais j’en ai parlé au cas où cela aiderait quelqu'un.
J'ajoute ces clés dans InfoPlist.strings
dans iOS 8.4, iPad mini 2. Cela fonctionne aussi. Je ne mets aucune clé, comme NSLocationWhenInUseUsageDescription
, dans mon Info.plist
.
InfoPlist.strings :
"NSLocationWhenInUseUsageDescription" = "I need GPS information....";
Base sur ce fil , dit-il, as in iOS 7
, peut être localisé dans InfoPlist.strings. Dans mon test, ces clés peuvent être configurées directement dans le fichier InfoPlist.strings
.
La première chose à faire est donc d’ajouter une ou les deux clés suivantes à votre fichier Info.plist:
- NSLocationWhenInUseUsageDescription
- NSLocationAlwaysUsageDescription
Ces deux clés prennent une chaîne décrivant pourquoi vous avez besoin de services de localisation. Vous pouvez entrer une chaîne du type "L'emplacement est requis pour savoir où vous vous trouvez" qui, comme dans iOS 7 , peut être localisée dans InfoPlist.strings. fichier.
UPDATE:
Je pense que la méthode de @IOS
est meilleure. Ajouter la clé à Info.plist
avec une valeur vide et ajouter des chaînes localisées à InfoPlist.strings
.