fellas,
I am developing a application in which I need functionality to ask user for their location more than once, what happening is when user allowed it once to use his location and when he navigate to another section it's not asking him to get his location, it's taking it from already cached location.
is that possible to ask user multiple time for his approval to get his location?
Any help appreciated.
Cheers,
Amit
You don't need to get permission multiple times.
To begin getting updates, you call:
[locationManager startUpdatingLocation];
Until you call [locationManager stopUpdatingLocation] you will get continuous location updates.
So, you need to implement the delegate method to say what happens when you get a new location. This delegate method could do something as simple as save the location to a class variable for later use.
The delegate function you need to implement is:
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {}
I should caution you here. It is battery intensive to constantly run the GPS (you'll kill the device in about 2.5 hours if you are requesting very accurate reading as fast as possible). So, what you should probably do is get a fix when the user turns the app on, and then call stopUpdatingLocation.
Then, in your app, have a "Locate Me" button, which would turn on the LocationManager, get a fix, and then turn off the LocationManager again. You might want to keep polling for a location until you get a good location.horizontalAccuracy
I suggest you implement an NSObject subclass, which implements the LocationManagerDelegate protocol. Then, this object would be shared across multiple view controllers. Here is a simple implementation of a central gpsController.
So, this would be gpsController.h:
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#interface gpsController : NSObject <CLLocationManagerDelegate> {
CLLocationManager *locationManager;
CLLocation *lastReading;
}
- (id)init;
#property (nonatomic, retain) CLLocationManager *locationManager;
#property (nonatomic, retain) CLLocation *lastReading;
#end
And then the following is gpsController.m:
#import "gpsController.h"
#implementation gpsController
#synthesize locationManager, lastReading;
- (id)init {
if(self = [super init]) {
[[self locationManager] startUpdatingLocation];
self.lastReading = nil;
}
return self;
}
- (CLLocationManager *)locationManager {
if (locationManager) return locationManager;
locationManager = [[CLLocationManager alloc] init];
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
locationManager.delegate = self;
return locationManager;
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
self.lastReading = newLocation;
}
According to iPhone Programming: The Big Nerd Ranch Guide, CLLocationManagers return the cached, last found device location first. Their sample code bails out of locationManager:didUpdateToLocation:fromLocation if the didUpdateToLocation timestamp is more than 3 minutes old, assuming it to be from the cache. That seems a pretty long time between valid updates, but maybe only by doing some testing would one know.
Maybe you could persist the timestamp between messages to see if you're still being served location data from the cache, or increment a counter for each call to locationManager:didUpdateToLocation:fromLocation and only ever use odd numbered calls or some other increment...
Related
I am completely new to iOS development so I may be doing this wrong but I have a class I am using to get coordinate gps data that I want to have as a generic class I can reuse in lots of apps. My problem is getting the data from the gps to properly display in other apps.
Here is my header file for the GPS class:
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#interface LocationAwareness : NSObject <CLLocationManagerDelegate> {
CLLocationManager *locationManager;
}
#property(copy) NSString *longitude;
#property(copy) NSString *latitude;
#end
And here is the implementation:
#import "LocationAwareness.h"
#implementation LocationAwareness
#synthesize longitude;
#synthesize latitude;
- (id)init {
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone; // whenever we move
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters; // 100 m
[locationManager startUpdatingLocation];
return self;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
// Stops updating location if data has been updated within 10 minutes
if ( abs([newLocation.timestamp timeIntervalSinceDate: [NSDate date]]) < 600) {
[locationManager stopUpdatingLocation];
float latitudedata = newLocation.coordinate.latitude;
latitude = [NSString stringWithFormat:#"%f", latitudedata];
float logitudedata = newLocation.coordinate.longitude;
longitude = [NSString stringWithFormat:#"%f", logitudedata];
}
}
#end
Now I can't seem to find anywhere that tells me how to get the latitude or longitude properties in another project. I have the header imported and have tried to store LocationAwareness.latitude into a variable that I can use but everything I store it in ends up blank. When I start my main class and aloc init a locationawareness object the gps fires up so I think its working but I don't seem to know enough about how this works to get everything in order. I've been searching the internet for hours. Anyone have an idea what I am doing wrong?
Well, this may or may not be causing the problem (it's quite likely), but a major problem is your init method.
The beginning should be:
self = [super init];
if (self) {
// Do your initializing as you did above.
}
return self;
edit:
I added your code with my update to a project and it works well.
In order to use this, you should do something like the following:
LocationAwareness *loc = [[LocationAwareness alloc] init];
// Give it some time to start updating the current location and then
// in a different function:
NSLog(#"%#", loc.latitude);
EDIT 2
Wherever you are using this, you will want to declare a property which stores it so that you can create it once and reference it many times. To do that, use the following code:
In the header for the object where you want to use this object, add this with the other properties:
#property (nonatomic, assign) LocationAwareness *location;
Then, towards the top of your implementation file (.m file) you should see other #synthesize lines, add this one:
#synthesize location;
Then, create the actual location instance that you want to use as per the example above:
self.location = [[LocationAwareness alloc] init];
Now give it some time to figure out your location and start providing updates. Then you can print the location like this:
NSLog(#"%#", self.location.latitude);
Let me just say I'm still an Objective-C/iOS noob, I haven't been learning it that long, so please excuse my ignorance :)
The content of my app changes based on the user's location. I would prefer the "ThisApp" would like to use your current location dialogue to appear before any content is loaded, to avoid the content having to reload.
I noticed that some apps seem to have that location popup appear while still on the launch image page (the page that shows Default.png). I did a lot of Googling but couldn't find anything (probably because I didn't know the correct terminology).
The way I implemented this was to have my app go to my LocationViewController first, this only contains default code, no changes, and in the storyboard it just has an image view with the Default.png in. Then I have a segue from the location controller (LocationViewControllerSegue) to the main view controller.
My AppDelegate.h looks like so:
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate, CLLocationManagerDelegate> {
CLLocationManager *locationManager;
}
#property (strong, nonatomic) UIWindow *window;
#end
And my AppDelegate.m:
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize window = _window;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if (locationManager == nil)
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer;
locationManager.distanceFilter = 16000;
[locationManager startUpdatingLocation];
return YES;
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
[self.window.rootViewController performSegueWithIdentifier:#"LocationViewControllerSegue" sender:self.window.rootViewController];
}
... snip a bunch of default generated code ...
#end
Is there a better way to do this, than my illusionary location view controller method?
If this is a reasonable way to get what I want, is there somewhere better I could place the performSegueWithIdentifier than in the locationManager? It seems like a bad place to put it (and not sure if it will cause issues further along in the app?), but moving it either into the LocationViewController or into didFinishLaunchingWithOptions: stops it from working.
Any help pointing me in the right direction would be appreciated. Thanks :)
This seems like a fairly hacky method, but one that might work. About the only thing I'd recommend for this particular method would be to have locationManager stop updating the location once you've found what you need ([locationManager stopUpdatingLocation]). Otherwise, as sree charan noted, you'll get a bunch of calls to locationManager:didUpdateToLocation:fromLocation: that will then generate a ton of calls to performSegueWithIdentifier:sender:.
As an aside, I think I might rather do this location update in your LocationViewController rather than the app delegate, but it likely has more to do with style than substance.
I am following a tutorial to integrate GPS to my application. I want to display the Latitude and Longitude values in the viewDidLoad method. According to the tutorial they have displayed it from another method. But i need it to be displayed in viewDidLoad. How can i modify the following code to display it in viewDidLoad ?
The HelloThereController.h view controller
#import "MyCLController.h"
#interface HelloThereViewController : UIViewController <MyCLControllerDelegate> {
MyCLController *locationController;
}
- (void)locationUpdate:(CLLocation *)location;
- (void)locationError:(NSError *)error;
#end
The HelloThereController.m view controller
#import "HelloThereViewController.h"
#implementation HelloThereViewController
- (void)viewDidLoad {
locationController = [[MyCLController alloc] init];
locationController.delegate = self;
[locationController.locationManager startUpdatingLocation];
//I need to print the latitude and logitude values here..
}
My MyCLController.h class
#protocol MyCLControllerDelegate
#required
- (void)locationUpdate:(CLLocation *)location;
- (void)locationError:(NSError *)error;
#end
#interface MyCLController : NSObject {
CLLocationManager *locationManager;
id delegate;
}
#property (nonatomic, retain) CLLocationManager *locationManager;
#property (nonatomic, assign) id delegate;
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation;
- (void)locationManager:(CLLocationManager *)manager
didFailWithError:(NSError *)error;
#end
My MyCLController.m class
#import "MyCLController.h"
#implementation MyCLController
#synthesize locationManager;
#synthesize delegate;
- (id) init {
self = [super init];
if (self != nil) {
self.locationManager = [[[CLLocationManager alloc] init] autorelease];
self.locationManager.delegate = self;
}
return self;
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
[self.delegate locationUpdate:newLocation];
}
- (void)locationManager:(CLLocationManager *)manager
didFailWithError:(NSError *)error
{
[self.delegate locationError:error];
}
- (void)dealloc {
[self.locationManager release];
[super dealloc];
}
#end
If this view controller loads as soon as the app loads, you can't.
it takes time for the location manager to get gps updates.
you'll need to display some ui component that lets the user know you're getting the location, such as UIActivityIndicatorView, and setup another method for displaying the coordinates when you get them.
In general you can accelerate the process sacrificing some accuracy setting the desiredAccuracy property. According to the documentation the values are:
kCLLocationAccuracyBestForNavigation
kCLLocationAccuracyBest
kCLLocationAccuracyNearestTenMeters
kCLLocationAccuracyHundredMeters
kCLLocationAccuracyKilometer
kCLLocationAccuracyThreeKilometers
Anyway you are interested in the first location, so still according to the documentation:
When requesting high-accuracy location data, the initial event delivered by the location
service may not have the accuracy you requested. The location service delivers the initial
event as quickly as possible. It then continues to determine the location with the accuracy
you requested and delivers additional events, as necessary, when that data is available.
You can try starting the location manager in the AppDelegate didFinishLaunchingWithOptions to have some more time before the view appears. As said by Frederick Cheung, locationManager.location can be nil, so it's better do the operations in the location manager delegate.
I'm a newbie to programming for iPhone, I'm following along this book.
I'm stuck at the example in Chapter 4, Delegation and Core Location.
Here's the code I've written so far:
WhereamiAppdelegate.h
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#interface WhereamiAppDelegate : NSObject <UIApplicationDelegate, CLLocationManagerDelegate> {
UIWindow *window;
CLLocation *locationManager;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#end
And here is the implementation file:
I have only included the changes that I've made.
The entire file is here.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Create location manager.
locationManager = [[CLLocation alloc] init];
[locationManager setDelegate:self];
[locationManager setDistanceFilter:kCLDistanceFilterNone];
[locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[locationManager startUpdatingLocation];
[self.window makeKeyAndVisible];
return YES;
}
- (void)dealloc
{
[locationManager setDelegate:nil];
[_window release];
[super dealloc];
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
NSLog(#"%#",newLocation);
}
- (void)locationManager:(CLLocationManager *)manager
didFailWithError:(NSError *)error
{
NSLog(#"Couldn't find loaction %#",error);
}
XCode gives me a warning that says CLLocation may not respond to setDistanceFilter and other similar warnings.
I am clueless here, I have followed the book line to line.
I think that I haven't implemented a necessary protocol or something.
Can someone please tell me what I'm doing wrong and how I should proceed further.
The class CLLocation is not the same as CLLocationManager. The former represents one location, while the latter is the manager class that handles configuring location updates for your application.
Things like setDistanceFilter: are undocumented parts of system APIs. They may change without warning in future versions of iOS (any), and they also disqualify your app from being in AppStore, but if you're fine with that, no problem... if Apple really wanted to hide those things, they wouldn't be findable by just making a for-loop on a method-table.
BTW, the difference between CLLocation and CLLocationManager is irrelevant for your situation, because the code actually runs and does the right thing, so, you are sending the correct messages to the correct objects.
First off, the disclaimer is I'm targeting iOS 5, so that very well may be the source of my issues, but if not...
I'm trying to write a simple class that manages location updates through CoreLocation. However, I see some pretty strange behavior. I have a custom class which basically wraps CLLocationManager and the delegate methods. This is the interface:
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#protocol CLZipCodeFetcherDelegate
#optional - (void)didReceiveZipCode:(NSString *)zipCode;
#end
#interface CLZipCodeFetcher : NSObject <CLLocationManagerDelegate> {
id <CLZipCodeFetcherDelegate> delegate;
CLLocationManager *locationManager;
}
#property (strong, readwrite) id <CLZipCodeFetcherDelegate> delegate;
#property (strong, read write) CLLocationManager *locationManager;
- (void)getZipCode;
#end
and the implementation (ignore the zip code related stuff- that's what the class is eventually meant to do, but right now I'm just trying to get some coordinates back from CoreLocation):
#import "CLZipCodeFetcher.h"
#implementation CLZipCodeFetcher
#synthesize delegate, locationManager;
- (id) init {
self = [super init];
if (self != nil) {
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.distanceFilter = 10.0f; // we don't need to be any more accurate than 10m
self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
self.locationManager.purpose = #"Retrieve zip code";
}
return self;
}
- (void)getZipCode {
[self.locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
NSLog(#"Received location");
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
NSLog(#"Location failed");
}
#end
And then I initialize an instance of the class in my view controller and call the method:
CLZipCodeFetcher *zipCodeFetcher = [[CLZipCodeFetcher alloc] init];
[zipCodeFetcher getZipCode];
Now, when I run this, the app takes a moment to load, and then pops up the alert dialog asking for permission to let the app use location services. However, as soon as it appears, it immediately disappears (too fast to let me hit the allow or deny button), and nothing happens (no delegate methods are called). If I then go into the Location Settings of the device, it indeed shows 'Off' for my app. When I turn it on manually from that screen and then go to try the app again, no alert dialog appears, but still nothing happens. Neither of my debug messages are logged. The behavior is the same on both the simulator and my iPhone 4.
So basically, anyone have any idea why? I've looked through all the similar threads, and many issues were related to memory management, but I don't think that'd be a problem here since ARC should be managing my CLLocationManager object, right?
P.S. Also, I did add the CoreLocation framework to my project, and I get no code errors or warnings, but is there anything else I need to do before using CoreLocation in my app?
ARC is deallocating your newly created object after the getZipCode method call.
I have not used ARC yet so I'm not sure of your syntax in the header (readwrite vs. read write).
But I would put an NSLog(#" my loc manager is %#", self.locationManager); inside of getZipCode just to make sure you've got one and it's not being released before it has a chance to do anything.