What's the easiest way to get the current loca

2019-01-07 04:40发布

I already know how to use the CLLocationManager, so I could do it the hard way, with delegates and all that.

But I'd like to have a convenience method that just gets the current location, once, and blocks until it gets the result.

5条回答
做个烂人
2楼-- · 2019-01-07 04:55

There are no "convenience methods" unless you code them yourself, but you'd still need to implement the delegate methods in whatever custom code you use to make things "convenient."

The delegate pattern is there for a reason, and as delegates are a big part of Objective-C, I recommend you get comfortable with them.

查看更多
叼着烟拽天下
3楼-- · 2019-01-07 04:57

I simplified and combined multiple answers to where the location is only updated if it's valid.

It also works under OSX as well as iOS.

This assumes the use-case where the current location is suddenly desired by the user. If it takes more than 100 ms in this example, it's considered an error. (Assumes the GPS IC &| Wifi (Apple's Skyhook clone) is already fired up and has a good fix already.)

#import "LocationManager.h"

// wait up to 100 ms
CLLocation *location = [LocationManager currentLocationByWaitingUpToMilliseconds:100];
if (!location) {
    NSLog(@"no location :(");
    return; 
}
// location is good, yay

https://gist.github.com/6972228

查看更多
戒情不戒烟
4楼-- · 2019-01-07 05:02

There is no such convenience and you shouldn't create your own. "Blocks until it gets the result" is extremely bad programming practice on a device like the iPhone. It can take seconds to retrieve a location; you should never make your users wait like that, and delegates ensure they don't.

查看更多
看我几分像从前
5楼-- · 2019-01-07 05:03

What I do is implement a singleton class to manage updates from core location. To access my current location, I do a CLLocation *myLocation = [[LocationManager sharedInstance] currentLocation]; If you wanted to block the main thread you could do something like this:

while ([[LocationManager sharedInstance] locationKnown] == NO){
   //blocking here
   //do stuff here, dont forget to have some kind of timeout to get out of this blocked    //state
}

However, as it has been already pointed out, blocking the main thread is probably not a good idea, but this can be a good jumping off point as you are building something. You will also notice that the class I wrote checks the timestamp on location updates and ignores any that are old, to prevent the problem of getting stale data from core location.

This is the singleton class I wrote. Please note that it is a little rough around the edges:

#import <CoreLocation/CoreLocation.h>
#import <Foundation/Foundation.h>

@interface  LocationController : NSObject <CLLocationManagerDelegate> {
    CLLocationManager *locationManager;
    CLLocation *currentLocation;
}

+ (LocationController *)sharedInstance;

-(void) start;
-(void) stop;
-(BOOL) locationKnown;

@property (nonatomic, retain) CLLocation *currentLocation;

@end
@implementation LocationController

@synthesize currentLocation;

static LocationController *sharedInstance;

+ (LocationController *)sharedInstance {
    @synchronized(self) {
        if (!sharedInstance)
            sharedInstance=[[LocationController alloc] init];       
    }
    return sharedInstance;
}

+(id)alloc {
    @synchronized(self) {
        NSAssert(sharedInstance == nil, @"Attempted to allocate a second instance of a singleton LocationController.");
        sharedInstance = [super alloc];
    }
    return sharedInstance;
}

-(id) init {
    if (self = [super init]) {
        self.currentLocation = [[CLLocation alloc] init];
        locationManager = [[CLLocationManager alloc] init];
        locationManager.delegate = self;
        [self start];
    }
    return self;
}

-(void) start {
    [locationManager startUpdatingLocation];
}

-(void) stop {
    [locationManager stopUpdatingLocation];
}

-(BOOL) locationKnown { 
     if (round(currentLocation.speed) == -1) return NO; else return YES; 
}

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
    //if the time interval returned from core location is more than two minutes we ignore it because it might be from an old session
    if ( abs([newLocation.timestamp timeIntervalSinceDate: [NSDate date]]) < 120) {     
        self.currentLocation = newLocation;
    }
}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
    UIAlertView *alert;
    alert = [[UIAlertView alloc] initWithTitle:@"Error" message:[error description] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
    [alert show];
    [alert release];
}

-(void) dealloc {
    [locationManager release];
    [currentLocation release];
    [super dealloc];
}

@end
查看更多
劳资没心,怎么记你
6楼-- · 2019-01-07 05:08

I appreciated the answer by Brad Smith. Implementing it I discovered that one of the methods he employs is deprecated as of iOS6. To write code that will work with both iOS5 and iOS6, use the following:

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
    if (abs([[locations lastObject] timeIntervalSinceDate:[NSDate date]]) < 120) {
        [self setCurrentLocation:[locations lastObject]];
    }
}

// Backward compatibility with iOS5.
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
    NSArray *locations = [NSArray arrayWithObjects:oldLocation, newLocation, nil];
    [self locationManager:manager didUpdateLocations:locations];
}
查看更多
登录 后发表回答