我已经知道如何使用CLLocationManager,因此我可以按照传统的方式使用委托等方法。但是我想要一个便捷的方法,只需获取当前位置一次并阻塞直到获得结果。
我已经知道如何使用CLLocationManager,因此我可以按照传统的方式使用委托等方法。但是我想要一个便捷的方法,只需获取当前位置一次并阻塞直到获得结果。
我所做的是实现一个单例类来管理来自核心位置的更新。 要访问我的当前位置,我执行以下操作: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
}
然而,正如已经指出的那样,阻塞主线程可能不是一个好主意,但这可以作为构建应用程序的一个很好的起点。您还会注意到,我编写的类检查位置更新的时间戳,并忽略任何旧的位置,以防止从核心位置获取过时数据的问题。
这是我编写的单例类。请注意,它还有一些不完善的地方:
#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
没有这样的便利,您也不应该自己创建。在像iPhone这样的设备上,“等待结果返回”是极其糟糕的编程实践。检索位置可能需要几秒钟时间,您绝不能让用户等待,而委托确保他们不必如此。
除非你自己编写,否则没有“方便方法”,但你仍然需要在任何自定义代码中实现委托方法以使事情变得“方便”。
委托模式存在的原因,在于委托是Objective-C的重要组成部分,我建议你熟悉它们。
我很感激Brad Smith的回答。在实施时,我发现他使用的其中一种方法已经在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];
}
我简化并合并多个答案,使得仅当位置有效时才更新位置。
它还可在OSX和iOS下使用。
这假设用户突然需要当前位置。如果在此示例中需要100毫秒以上,则认为是错误。(假设GPS IC & | Wifi (Apple's Skyhook clone)已经启动并已有良好的修复。)
#import "LocationManager.h"
// wait up to 100 ms
CLLocation *location = [LocationManager currentLocationByWaitingUpToMilliseconds:100];
if (!location) {
NSLog(@"no location :(");
return;
}
// location is good, yay