2009-01-19 10 views
22

Ya sé cómo usar CLLocationManager, así que podría hacerlo de la manera difícil, con delegados y todo eso.¿Cuál es la forma más fácil de obtener la ubicación actual de un iPhone?

Pero me gustaría tener un método práctico que simplemente obtenga la ubicación actual, una vez, y bloques hasta que obtenga el resultado.

+0

Debe aclarar la pregunta: ¿Si sabe cómo utilizar CLLocationManager - ¿Cuál es el problema al implementar un "método de conveniencia"? – tcurdt

+10

Creo que el problema es que no es tan fácil. Normalmente, primero devuelve una ubicación anterior, seguida de una muy inexacta, seguida de otras progresivamente mejores, ¿en qué momento decides que tienes la que quieres? Depende de ti. – rustyshelf

+0

¡Buen punto, oxidado! –

Respuesta

4

No hay "métodos de conveniencia" a menos que los codifique usted mismo, pero igual tendría que implementar los métodos de delegado en cualquier código personalizado que use para hacer las cosas "convenientes".

El patrón de delegados está ahí por una razón, y como los delegados son una gran parte de Objective-C, le recomiendo que se sienta cómodo con ellos.

45

Lo que hago es implementar una clase singleton para administrar las actualizaciones desde la ubicación central. Para tener acceso a mi ubicación actual, hago un CLLocation *myLocation = [[LocationManager sharedInstance] currentLocation]; Si quisiera bloquear el hilo principal se podría hacer algo como esto:

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 
} 

Sin embargo, como ya se ha señalado, bloqueando el hilo principal no es probablemente una buena idea, pero este puede ser un buen punto de partida a medida que construyes algo. También notará que la clase que escribí verifica la marca de tiempo en las actualizaciones de ubicación e ignora las que son antiguas, para evitar el problema de obtener datos obsoletos desde la ubicación del núcleo.

Esta es la clase singleton que escribí. Tenga en cuenta que es un poco difícil en los bordes:

#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 
+0

Gracias por esta solución. Es un pequeño controlador agradable tener disponible.Sin embargo, una cosa que encontré fue que el método locationKnown no era muy preciso: siempre devolvía SÍ porque currentLocation se inicializa en el método init. Aquí hay una versión alternativa del método que usa la propiedad de velocidad para determinar si se ha recibido la ubicación del usuario: - (BOOL) locationKnown { if (round (currentLocation.speed) == -1) return NO; else return SÍ; } –

+3

He editado su código, espero haberlo entendido correctamente. –

+2

Usted solo ha asignado los recursos pero no los ha liberado. ¿Puede decirme por favor dónde lo lanzaría que no causará ningún problema? –

7

No existe tal conveniencia y usted no debe crear la suya propia. "Bloquea hasta obtener el resultado" es extremadamente malo práctica de programación en un dispositivo como el iPhone. Puede llevar unos segundos recuperar una ubicación; nunca debe hacer que sus usuarios esperen así, y los delegados se aseguran de que no lo hagan.

+1

¿No es eso para qué son los hilos? –

+0

Hilos, sí, cuando se combina con una devolución de llamada al finalizar. Donde la devolución de llamada está programada en el bucle de ejecución de origen. Lo que le proporciona una interfaz exactamente igual a la que ya tiene el delegado de CLLocationManager. "Bloquear hasta que termine" nunca es un buen diseño en las aplicaciones para el usuario final. –

+18

No estoy de acuerdo, "bloques hasta que se haga" tiene muchos usos legítimos en todo tipo de aplicaciones, y no es sinónimo de "congelar la interfaz de usuario hasta que esté listo". Tratar de hacer * todo * asincrónico solo lleva a comportamientos extraños y aplicaciones con errores. ¿A quién le importa cuán rápido aparece/aparece la IU si la información que necesita mostrar aún no está disponible? Las operaciones asincrónicas tienen su lugar, y también lo hacen las de bloqueo. No es un caso donde uno es bueno y el otro malo. – aroth

0

Agradezco la respuesta de Brad Smith. Implementándolo Descubrí que uno de los métodos que emplea está obsoleto a partir de iOS6. Para escribir código que funcione tanto con iOS5 y iOS6, utilice la siguiente:

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

Simplifiqué y combinar múltiples respuestas a la ubicación donde sólo se actualiza si es válido.

También funciona con OSX e iOS.

Esto asume el caso de uso en el que el usuario desea de repente la ubicación actual. Si lleva más de 100 ms en este ejemplo, se considera un error. (Asume el GPS IC & | Wifi (clon Skyhook de Apple) que ya se puso en marcha y tiene una buena solución ya.)

#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

+0

¡Funciona bien! –

Cuestiones relacionadas