CLLocationManager Обратный вызов AuthorizationStatus?
В моем приложении у меня есть вкладка под названием "Откройте". Вкладка "Обнаружение" будет использовать текущее местоположение пользователей для поиска "материала" рядом с ними. Вместо того, чтобы предоставлять пользователю общий запрос авторизации (который обычно отклоняется, основываясь на исследованиях), я предоставляю пользователю модальное объяснение того, что мы просим. Если они говорят "да", то появляется фактическое сообщение об авторизации.
Тем не менее, пользователь по-прежнему может отказаться от приглашения. Есть ли способ добавить обратный вызов в приглашение, чтобы, как только пользователь выбрал опцию, я мог видеть, приняли ли они или отклонили?
Я попробовал это:
func promptForLocationDataAccess() {
locationManager.requestWhenInUseAuthorization()
println("test")
}
Как и ожидалось, "println" выполняется одновременно с запросом запроса, поэтому я не могу сделать это таким образом.
Проблема заключается в том, что если пользователь решит не использовать данные о местоположении, обслуживаемый контент будет отличаться от принятого.
В идеале я надеюсь на какой-нибудь обратный вызов, но сейчас я выберу любое логическое направление!
7 ответов
Вы можете использовать locationManager:didChangeAuthorizationStatus:
CLLocationManagerDelegate
метод как "обратный вызов" сортов.
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
if (status == kCLAuthorizationStatusDenied) {
// The user denied authorization
}
else if (status == kCLAuthorizationStatusAuthorized) {
// The user accepted authorization
}
}
И в Swift (обновление, предложенное пользователем Michael Marvick, но по какой-то причине отклоненное...):
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if (status == CLAuthorizationStatus.denied) {
// The user denied authorization
} else if (status == CLAuthorizationStatus.authorizedAlways) {
// The user accepted authorization
}
}
Когда изменяется статус авторизации для местоположения, метод делегата didChangeAuthorizationStatus:
будет называться.
Когда вы звоните requestWhenInUseAuthorization
в первый раз после установки приложения будет вызван метод делегата со статусом kCLAuthorizationStatusNotDetermined
(0).
Если пользователь отклоняет доступ к службам определения местоположения, метод делегата будет вызван снова со статусом kCLAuthorizationStatusDenied
(2).
Если пользователь одобряет доступ к службам определения местоположения, метод делегата будет вызван снова со статусом kCLAuthorizationStatusAuthorizedAlways
(3) или kCLAuthorizationStatusAuthorizedWhenInUse
(4) в зависимости от разрешения, которое было запрошено.
При последующих выполнениях вашего приложения метод делегата получит статус kCLAuthorizationStatusDenied
или же kCLAuthorizationStatusAuthorizedAlways
/kCLAuthorizationStatusAuthorizedWhenInUse
после звонка requestWhenInUseAuthorization
на основе текущего состояния разрешения служб определения местоположения для приложения в настройках устройства.
Свифт 3
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if (status == CLAuthorizationStatus.denied) {
// The user denied authorization
} else if (status == CLAuthorizationStatus.authorizedAlways) {
// The user accepted authorization
}
}
Это решение не является лучшим во всех сценариях, но оно сработало для меня, поэтому я решил поделиться:
import Foundation
import CoreLocation
class LocationManager: NSObject, CLLocationManagerDelegate {
static let sharedInstance = LocationManager()
private var locationManager = CLLocationManager()
private let operationQueue = OperationQueue()
override init(){
super.init()
//Pause the operation queue because
// we don't know if we have location permissions yet
operationQueue.isSuspended = true
locationManager.delegate = self
}
///When the user presses the allow/don't allow buttons on the popup dialogue
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
//If we're authorized to use location services, run all operations in the queue
// otherwise if we were denied access, cancel the operations
if(status == .authorizedAlways || status == .authorizedWhenInUse){
self.operationQueue.isSuspended = false
}else if(status == .denied){
self.operationQueue.cancelAllOperations()
}
}
///Checks the status of the location permission
/// and adds the callback block to the queue to run when finished checking
/// NOTE: Anything done in the UI should be enclosed in `DispatchQueue.main.async {}`
func runLocationBlock(callback: @escaping () -> ()){
//Get the current authorization status
let authState = CLLocationManager.authorizationStatus()
//If we have permissions, start executing the commands immediately
// otherwise request permission
if(authState == .authorizedAlways || authState == .authorizedWhenInUse){
self.operationQueue.isSuspended = false
}else{
//Request permission
locationManager.requestAlwaysAuthorization()
}
//Create a closure with the callback function so we can add it to the operationQueue
let block = { callback() }
//Add block to the queue to be executed asynchronously
self.operationQueue.addOperation(block)
}
}
Теперь каждый раз, когда вы хотите использовать информацию о местоположении, просто окружите ее следующим:
LocationManager.sharedInstance.runLocationBlock {
//insert location code here
}
Поэтому всякий раз, когда вы пытаетесь использовать информацию о местоположении, проверяется статус авторизации. Если у вас еще нет разрешения, он запрашивает разрешение и ждет, пока пользователь не нажмет кнопку "Разрешить" или кнопку "Не разрешать". Если нажать кнопку "Разрешить", любые запросы данных о местоположении будут обрабатываться в отдельных потоках, но если нажать кнопку "Не разрешать", все запросы местоположения будут отменены.
Рабочий код в Swift 5:
Plist: добавить запись
<key>NSLocationWhenInUseUsageDescription</key>
<string>Needs Location when in use</string>
import UIKit
import CoreLocation
class ViewController: UIViewController {
var locationManager: CLLocationManager?
override func viewDidLoad() {
super.viewDidLoad()
locationManager = CLLocationManager()
//Make sure to set the delegate, to get the call back when the user taps Allow option
locationManager?.delegate = self
}
}
extension ViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .notDetermined:
print("not determined - hence ask for Permission")
manager.requestWhenInUseAuthorization()
case .restricted, .denied:
print("permission denied")
case .authorizedAlways, .authorizedWhenInUse:
print("Apple delegate gives the call back here once user taps Allow option, Make sure delegate is set to self")
}
}
}
Цель С
Для обратного вызова блока на didChangeAuthorizationStatus добавьте это в .h
@property void(^authorizationCompletionBlock)(BOOL);
и следуя в. м
-(void)locationManager:(CLLocationManager *)locationManager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
_authorizationStatus = status;
switch (status) {
case kCLAuthorizationStatusAuthorizedAlways:
case kCLAuthorizationStatusAuthorizedWhenInUse:
if (self.authorizationCompletionBlock) {
self.authorizationCompletionBlock(YES); // this fires block
}
default:
if (self.authorizationCompletionBlock) {
self.authorizationCompletionBlock(NO); // this fires block
}
break;
}
}
и добавьте обработчик так:
// this listens block
// in your VC or Utility class
authorizationCompletionBlock = ^(BOOL isGranted) {
completionBlock(isGranted);
};
Swift 3.2
var authorizationCompletionBlock:((Bool)->())? = {_ in}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch (status)
{
case (.authorizedWhenInUse):
if authorizationCompletionBlock != nil
{
authorizationCompletionBlock!(true)
}
default:
if authorizationCompletionBlock != nil
{
authorizationCompletionBlock!(false);
}
}
}
и обработчик, как это
authorizationCompletionBlock = { isGranted in
print(isGranted)
}
По аналогии с ответом tdon выше, я создал функцию с блоком завершения, чтобы сообщать о состоянии после его получения с устройства:
func retrieveAuthorizationStatus(completion: @escaping (TrackingState) -> ()) {
let status = CLLocationManager.authorizationStatus()
switch status {
case .authorizedWhenInUse:
completion(.off)
default:
completion(.issue)
}
}
TrackingState - это отдельное перечисление, которое я использую для управления отображением в контроллере представления. Вы также можете легко передать authorizationStatus() в блоке завершения:
func retrieveAuthorizationStatus(completion: @escaping (CLAuthorizationStatus) -> ()) {
let status = CLLocationManager.authorizationStatus()
completion(status)
}