views:

5715

answers:

5

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

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

+3  A: 

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.

August
+7  A: 

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: 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 allready pointed out, blocking the main thread is probabaly 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)
            [[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];
}
@end
Brad Smith
Thanks for this solution. It is a nice little controller to have available. One thing i found though was that the locationKnown method wasn't very accurate - it always returned YES because currentLocation is initialised in the init method. Here is an alternative version of the method that uses the speed property to determine whether the user's location has been received:-(BOOL) locationKnown { if (round(currentLocation.speed) == -1) return NO; else return YES;}
Benjamin Pearson
+1  A: 

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.

Chris Hanson
Isn't that what threads are for?
Roger Nolan
Threads, yes, when combined with a callback on completion. Where the callback is scheduled on the originating run loop. Which gets you an interface exactly like the CLLocationManager's delegate already has. "Block until done" is never a good design in end-user apps.
Chris Hanson
A: 

ok, so then how would you go about ensuring that the locationmanager starts updating BEFORE your first view is loaded?

Why would you want to do that?Better to load your first view and then update it when the location comes in - through the delegate method.
Roger Nolan
Well, the first view is highly dependant on the location. Its at the core of the app, either way there has to be a spinner of some kind at least for my app because there HAS to be data in the first view that loads
A: 

Its not returning correct longitude and latitude for UK region..

bond