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)
}
Другие вопросы по тегам