CLLocationManagerの使用方法はすでに知っているので、デリゲートなどを使って難しい方法でそれを行うことができました。
しかし、現在の場所を1回だけ取得し、結果が得られるまでブロックする便利なメソッドが必要です。
私がしているのは、コアロケーションからの更新を管理するためのシングルトンクラスを実装することです。現在地にアクセスするには、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の大部分を占めるため、慣れることをお勧めします。
場所が有効な場合にのみ更新される場所について、複数の回答を簡略化して組み合わせました。
また、iOSだけでなくOSXでも動作します。
これは、現在の場所がユーザーによって突然望まれるユースケースを想定しています。この例で100ミリ秒以上かかる場合は、エラーと見なされます。 (GPS IC&| Wifi(AppleのSkyhookクローン)がすでに起動されており、適切な修正が行われていることを前提としています。)
#import "LocationManager.h"
// wait up to 100 ms
CLLocation *location = [LocationManager currentLocationByWaitingUpToMilliseconds:100];
if (!location) {
NSLog(@"no location :(");
return;
}
// location is good, yay
ブラッド・スミスの答えに感謝します。それを実装すると、彼が採用している方法の1つが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];
}