iOS Waiting for Location Manager to Init

感情迁移 提交于 2019-12-21 12:17:40

问题


I'm developing an iOS app based on the map and location tracking. When the user first boots the app it asks for permission to track location etc. just fine. The only problem is while it's doing that, I have code setting up the initial map view and other location-based variables before the user has clicked OK.

I've found I can put these initiation steps after a while loop that waits for the user to change the location manager's permissions as below but this can't possibly be best practice, not to mention it leads to some strange behavior in the transition between the splash screen and the map:

BOOL firstrun = TRUE;
while ([[locationManager class] authorizationStatus] == kCLAuthorizationStatusDenied || [[locationManager class] authorizationStatus] == kCLAuthorizationStatusNotDetermined) {
    NSLog(@"Waiting for location permission");
}
...initiation code...

Is there a "location access granted" listener for the alert box or a similar function in the location manager delegate I don't know about? I see no such method in the docs. Anyone know what the best practice is here? Thank you so much.

EDIT I start my location tracking as follows:

if (nil == locationManager)
    locationManager = [[CLLocationManager alloc] init];

[locationManager startMonitoringSignificantLocationChanges];

self.musicmap.delegate = self;
[self.mymap setShowsUserLocation:true];

Thanks


回答1:


I would recommend making your class a CLLocationManagerDelegate and then implementing this method:

- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
    //Your code goes here
}

More information about CLLocationManagerDelegate can be found here.

Hope that helps!




回答2:


I had a similar problem with my application and the app doing things before the user has time to accept or decline the location permission dialogue. Here is what I ended up doing.

-(BOOL)locationAuthorizationStatus {
    if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied) {
        // user has not authorized us to use location
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Location Denied", @"Location Denied")
                                                        message:NSLocalizedString(@"This app does not have permission to access your location. Please enable location access in device settings.", @"Message stating this app does not have permission to access your location and to enable location permission in settings")
                                                       delegate:self
                                              cancelButtonTitle:NSLocalizedString(@"Ok", @"Ok")
                                              otherButtonTitles: nil];
        [alert show];
        return NO;
    }

    // Check if region monitoring is available for this device
    if (![CLLocationManager regionMonitoringAvailable]) {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Geofencing Unavailable", @"Geofencing Unavailable")
                                                        message:NSLocalizedString(@"This device is not able to monitor regions", @"Message stating this device is not able to monitor regions")
                                                       delegate:nil
                                              cancelButtonTitle:nil
                                              otherButtonTitles:NSLocalizedString(@"Ok", @"Ok"), nil];
        [alert show];
        return NO;
    } else {
        if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined) {
            // trigger a location check to prompt user for authorization
            LocationManagerController *locationController = [LocationManagerController sharedManager];
            [locationController.locationManager startUpdatingLocation];
            // the dialogue box is triggered here
            [locationController.locationManager stopUpdatingLocation];
            _waitingOnAuthorization = YES;
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(checkForAuthorizationStatusChange) name:@"WaitingOnAuthorizationStatus" object:nil];
            return NO;
        }
    }

    return YES;
}

-(void)checkForAuthorizationStatusChange {
    if (_waitingOnAuthorization) {
        // this should only catch location change on first time
        if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorized) {
            // user approved location services

        } else {
            // user declined authorization

        }
        // set flag back to NO
        _waitingOnAuthorization = NO;
    }

    // remove our notification observer
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

You will have to add the variables that apply to your use case. But here is the gist of it.

  1. Check for authorization status via BOOL method (locationAuthorizationStatus)
  2. If ok to use location, returns YES do whatever you want
  3. If not, returns NO and alerts user
  4. If first time, will fire up the location manager to trigger location dialogue, then stop it to save battery, sets the flag and sets a notification so you will know when the user has hit yes or no.
  5. The notification fires the method checkAuthorizationChangeStatus and rechecks permissions to see what the user did. From there, you can call any methods you need based on the users choice.

Apple does not have any delegate methods to catch this selection, so the only way around it is kinda of hacky. This method has worked well for me. Slightly hacky, but works. Hope this helps.




回答3:


//Start up motion manager, not sure if you need this for location manager
    motionManager = [[CMMotionManager alloc] init];
    if (motionManager.accelerometerAvailable) {
        motionManager.accelerometerUpdateInterval = 1.0/2.0;
        [motionManager startAccelerometerUpdates];
    }

    locationManager = [[CLLocationManager alloc] init];

    //We will be the location manager delegate
    locationManager.delegate = self;

    //Track position at the 100m accuracy
    locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;

    //We want to see all location updates, regardless of distance change
    locationManager.distanceFilter = 0.0;

    [locationManager startUpdatingLocation];

Put the above somewhere in your "viewDidLoad" or appDelegate "didFinishLaunchingWithOption"

The following handles the updates and stuff. bestLocation is a CLLocation. You'll need to implement CLLocationManagerDelegate. TWO_MINUTES = 120

- (BOOL)isBetterLocation:(CLLocation *)location {
    if (bestLocation == nil){
        //best location not set yet, so it's a better location by default
        return YES;
    }

    // Figure out how long it's been since we got a better location
    NSTimeInterval timeDelta = [location.timestamp timeIntervalSinceDate:bestLocation.timestamp];

    BOOL isSignificantlyNewer = timeDelta > TWO_MINUTES;

    BOOL isSignificantlyOlder = timeDelta < -TWO_MINUTES;

    BOOL isNewer = timeDelta > 0;

    if (isSignificantlyNewer) {
        return YES;
    }else if (isSignificantlyOlder) {
        return NO;
    }

    CLLocationAccuracy accuracyDelta = location.horizontalAccuracy - bestLocation.horizontalAccuracy;
    //You want accuracy to be low
    BOOL isLessAccurate = accuracyDelta > 0;
    BOOL isMoreAccurate = accuracyDelta < 0;
    BOOL isDifferent = location.coordinate.latitude != bestLocation.coordinate.latitude || 
    location.coordinate.longitude != bestLocation.coordinate.longitude;

    if (isMoreAccurate) {
        return YES;
    } else if (isNewer && !isLessAccurate && isDifferent) {
        return YES;
    }

    return NO;
}






#pragma mark - Location manager delegate
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
    if ([self isBetterLocation:newLocation]){
        self.bestLocation = newLocation;

    } else {
        [locationManager stopUpdatingLocation];
        isLocating = NO;
        NSLog(@"AppDelegate: Turning off location manager >>>>>>>>>>>>>>>>>>>>>");
    }
}

Note: You don't need the "isBetterLocation" method. That will just check the location and turn off the location manager after like 2 minutes so you don't wear down the user's battery. Sometimes you don't want the manager off though, so you can leave it out if you want.

I don't know how much of this you've done already. This is the way I did it.



来源:https://stackoverflow.com/questions/13981679/ios-waiting-for-location-manager-to-init

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!