Я уже знаю, как использовать 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, я рекомендую вам с ними комфортно.
Я оценил ответ Брэда Смита. Внедряя это, я обнаружил, что один из методов, которые он использует, устарел с 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 мс, он считает ошибку. (Предполагается, что IC 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