获得iPhone当前位置的最简单方法是什么?

时间:2021-09-26 14:03:43

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

我已经知道如何使用CLLocationManager,所以我可以用委托和所有的方法来做。

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

但是我希望有一个方便的方法,它只获取当前位置一次,然后阻塞,直到得到结果。

5 个解决方案

#1


45  

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:

我所做的是实现一个单例类来管理来自核心位置的更新。为了访问当前位置,我执行CLLocation *myLocation = [[LocationManager sharedInstance] currentLocation];如果你想阻止主线程,你可以这样做:

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

#2


7  

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.

没有这种方便,你不应该创建自己的。在iPhone这样的设备上,“阻塞直到得到结果”是极其糟糕的编程实践。检索位置可能需要几秒钟;你不应该让你的用户那样等待,委托确保他们不会。

#3


4  

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.

委托模式的存在是有原因的,由于委托是Objective-C的重要组成部分,我建议您熟悉它们。

#4


0  

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:

我很欣赏布拉德·史密斯的回答。实现它时,我发现他使用的方法之一是不赞成iOS6的。要编写与iOS5和iOS6兼容的代码,请使用以下代码:

- (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];
}

#5


0  

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.

它也适用于OSX和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.)

这假设用户突然需要当前位置的用例。如果在本例中需要超过100ms,则视为错误。(假设GPS IC和| Wifi(苹果的Skyhook克隆产品)已经启动,并且已经得到了很好的修复。)

#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

https://gist.github.com/6972228

#1


45  

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:

我所做的是实现一个单例类来管理来自核心位置的更新。为了访问当前位置,我执行CLLocation *myLocation = [[LocationManager sharedInstance] currentLocation];如果你想阻止主线程,你可以这样做:

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

#2


7  

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.

没有这种方便,你不应该创建自己的。在iPhone这样的设备上,“阻塞直到得到结果”是极其糟糕的编程实践。检索位置可能需要几秒钟;你不应该让你的用户那样等待,委托确保他们不会。

#3


4  

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.

委托模式的存在是有原因的,由于委托是Objective-C的重要组成部分,我建议您熟悉它们。

#4


0  

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:

我很欣赏布拉德·史密斯的回答。实现它时,我发现他使用的方法之一是不赞成iOS6的。要编写与iOS5和iOS6兼容的代码,请使用以下代码:

- (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];
}

#5


0  

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.

它也适用于OSX和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.)

这假设用户突然需要当前位置的用例。如果在本例中需要超过100ms,则视为错误。(假设GPS IC和| Wifi(苹果的Skyhook克隆产品)已经启动,并且已经得到了很好的修复。)

#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

https://gist.github.com/6972228