My application doesn't need location service to constantly monitor user's location and I really don't want to drain battery, so on startup I get the location, send it to statistics server and call stopUpdatingLocation on my CLLocationManager instance. Icon disappears from status bar! :)
Next, in one of my tabs I have MKMapView in which user can ask for and see (using annotation) his current location. After I switch to another tab with different view I'd like to stop using location services. How to achieve this? I've read SO question What determines the presence of the iPhone Location Services icon in the status bar? and now I'm about to start thinking described use case is a bug in iOS (?).
EDIT: in view with MKMapView I don't use CLLocationManager at all.
In the viewDidDisappear method in the view controller that has the MKMapView, set the mapview's showsUserLocation property to NO.
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
self.mapView.showsUserLocation = NO;
}
Related
Basically when my app launches for the first time the Enable Location Services prompt appears. When the user taps on Allow I would like to start updating user location and zoom into the region.
In my viewDidLoad I start the location manager, but unfortunately the view is loaded before the user has a chance to tap on Allow. Everything works fine on second launch of the application because the user would have allowed location services already
My question is how can I capture the event of tapping on Allow so I can run the code to zoom into a region?
I have tried using -(void)locationManager:didChangeAuthorizationStatus: but it doesn't seem to call this delegate method when the user taps on allow.
Hope this makes sense I am very new to this.
To my knowledge you cannot, but you don't have to capture this event, because you won't be able to zoom to the certain location before you get the coordinates of this location. Your app works fine on the second launch, because it uses cached location data from the first start. So, what you need is to run your zooming code after you received your new valid coordinates.
If you use CLLocationManager, than look at
– locationManager:didUpdateToLocation:fromLocation:
in its delegate. If user denied to use location services, that your delegate will receive
locationManager:didFailWithError:
With corresponding error.
If you use MKMapKit, than in MKMapView delegate implement method
– mapViewWillStartLocatingUser:
to focus on current user position.
to handle denial implement
– mapView:didFailToLocateUserWithError:
Links to corresponding Apple documentation:
CLLocationManager
CLLocationManagerDelegate
MKMapViewDelegate
Here it works just fine. I initiate the location manager, then I set it's delegate and start it. When the popup to allow comes up, the -(void)locationManager:didChangeAuthorizationStatus: is called with CLAuthorizationStatus equals to kCLAuthorizationStatusNotDetermined. If I tap "Dont' Allow", it's called again with CLAuthorizationStatus equals to kCLAuthorizationStatusDenied. When tapping "Allow", it's called with CLAuthorizationStatus equals to kCLAuthorizationStatusAuthorized. Check if your delegate is set correctly.
You can handle it like this :
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
switch([CLLocationManager authorizationStatus])
{
case kCLAuthorizationStatusAuthorized:
NSLog(#"Location services authorised by user");
break;
case kCLAuthorizationStatusDenied:
NSLog(#"Location services denied by user");
break;
case kCLAuthorizationStatusRestricted:
NSLog(#"Parental controls restrict location services");
break;
case kCLAuthorizationStatusNotDetermined:
NSLog(#"Unable to determine, possibly not available");
break;
}
}
I set the mapView's showUserLocation property to YES, and it does nothing. I was able to use a CLLocationManager to center the map on the user's current location, but there's no pin/dot on the screen. How can I put a pin on the map for the exact location of the user?
This has messed me up several times but its an easy fix
The problem is that your code is:
[self.mapView showUserLocation:YES];
Should be:
[self.mapView setShowsUserLocation:YES]
Also make sure you have set the delegate as well
IN Nib name you can set showuserlocation to mark for Mapview.
Basically what I am trying to accomplish is an augmented reality application. I have a map view and the augmented reality view.
When the user only looks at the map view and then returns to the previous page in the UINavigation stack all location services are stopped and the arrow toolbar notification dissapears. It is when the user leaves the map view and the UIImagePickerController is presented modally the location services notification will remain even after the user presses the button that is responsible for stopping all location services and popping the current view.
I know it is not my CLLocationManager causing the problem because as I said the error doesn't occur when the UIImagePicker is never placed on the screen. My thoughts are that the location services used for geolocating or whatever the camera uses them for is not stopping even though i dimiss the modal view before popping the current view.
For the life of me I can't figure out why they arent stopping, if anyone might know why it would be a huge help.
Here is the code that I have right now in the method that is called to prepare for popping the view from the UINavigation stack
[_locationManager setDelegate:nil];
[_locationManager stopUpdatingLocation];
[_locationManager stopUpdatingHeading];
[[UIAccelerometer sharedAccelerometer] setDelegate:nil];
if (_imagePickerOn){
[self dismissModalViewControllerAnimated:YES];
_imagePickerOn = FALSE;
}
--EDIT--
Heres the method where I present the image picker, very basic:
- (IBAction) cameraButtonPressed{
_imagePickerOn = TRUE;
[self presentModalViewController:_imagePicker animated:NO];
}
I assume you’re allocating the image picker controller in advance elsewhere. That’s probably the problem—when its view goes offscreen (as you dismiss it), it’s not getting deallocated, so it’s still in memory and still presumably using its location manager. It’s a bug, but not your bug, so you’re not going to be able to do much about it with your current setup.
A more common pattern is to only allocate things like UIImagePickerController when you’re about to present them and to release them immediately after the call to -presentModalViewController:animated:. It can make your UI a little less responsive, especially when allocating complicated view controller (I’m not sure if the image picker controller qualifies as such), but you get the benefit of reduced memory usage and—hopefully—of no longer using location services when you don’t want them.
I have a created a small app which uses location services on the iPhone. All works well, except the fact, that sometimes, the small arrow in the info-bar stays active even if I explicitly kill the app.
I use the background mode for locationservices, thus the appDelegate methods applicationWillResignActive, applicationDidEnterBackground, applicationWillEnterForeground and applicationDidBecomeActive are implemented but do not touch the location services (well - I need them in background mode).
In that configuration applicationWillTerminate is never called; I implemented all the cleanup cleanup as stopUpdatingLocation in dealloc, as I did not find any other place appropriate for this. But still - the indicator stays on.
Any ideas?
I had the same problem - app leaving the location indicator on in the status bar.
My problem turned out to be that I had originally called the 'startMonitoringSignificantLocationChanges' method of CCLocationManager thinking that would give rough location info that I could up the resolution on when I really needed it.
Unfortunately once an app has called that method once, even if you then delete the app and re-install it it will always re-show the icon in the status bar until the app calls 'stopMonitoringSignificantLocationChanges' on CCLocationManager to unregister itself from the system - complete pain as I have to leave that code in until it has sorted itself out on the several people who were testing my app for me.
So if you get that icon stuck on with your app make sure you've matched any calls to 'startMonitoringSignificantLocationChanges' with a stop call.
if you started your location manager job with
[MyLocationManagerInstance startMonitoringForSignificantLocationChanges];
then you need to stop it with:
[MyLocationManagerInstance stopMonitoringForSignificantLocationChanges];
If you force the termination of the application, applicationWillTerminate isn't called, as, for the OS point of view, it appears as a SIGKILL.
I met a similar problem. The problem only happens on iPhone4, but works perfect on my 3GS. After looking into my code, I found that in my startUpdatingLocation method I used startUpdatingLocation and startMonitoringForRegion services, however, in my stopUpdatingLocation method I just stop updatingLocation service. I fixed this issue by turning off MonitoringForRegion as well.
#pragma mark -
#pragma mark Location methods
- (void)startUpdatingLocation
{
[self.locationManager startUpdatingLocation];
[self.locationManager startMonitoringForRegion:desRegion desiredAccuracy:50.0f];
}
- (void)stopUpdatingLocation
{
[self.locationManager stopUpdatingLocation];
// !!!: fix location service indicator stuck issue
[self.locationManager stopMonitoringForRegion:desRegion];
}
Ok, problem solved. The indicator will stay on until a new location is found. Then if everything else is correct, the indicator turns off.
I have two views in my app, one is a general view where CoreLocation works away calculating the users location while the user is doing other stuff in the view. The second view is accessed by the user when they touch a button allowing them to locate themselves more accurately using a mapview and MapKit, i would like the mapview in this view to show the location that CoreLocation has already identified in the first view AND to continue displaying this location based on updates from CoreLocation in the other view.
Is the best way here to create a singleton that encapsulates the CoreLocation stuff and have this referenced in the view with the map, or to use notifications ? or to use some other better practice for my scenario ?
Thanks
I have a couple of apps that use CoreLocation in multiple places. From what I've read, you definitely want there to be just one instance of CLLocationManager. It's worked great as a singleton for me.
Hope this helps!
If I were you, I would do it this way:
Decide which view is going to be always loaded.
I assume, you want CalculatingView is loaded all the time, and MapView will be loaded/unloaded based on the user action.
Allocate and initialize a pointer to CLLocationManager inside CalculatingView. This will provide location property and also call delegate messages. Since the CalculatingView is loaded and retained, this pointer is always working too.
Set CLLocationManager's delegate to be CalculatingView, which might also be called self, if this view has allocated and initialized CLLocationManager pointer.
Implement delegate methods of CLLocationManager, in CalculatingView
If you like to, you can have MapView to be allocated and initialized within CalculatingView. But it's ok to have it in other places, as long as you can send message to MapView. Make sure they are valid by checking if it's not nil or if it respondsToSelector.
When the CLLocationManager's delegate, which is CalculatingView receives messages, send a message to MapView.
It's like relaying messages, but the messages that MapView should respond to don't have to be the same messages sent to CalculatingView like delegate method calls from CLLocationManager
By checking if MapView is valid, meaning if it's loaded to be displayed, you can decide to send messages to MapView or not
The essence is to decide which view is loaded consitently, to use delegate methods for sending(or relaying) messages to other pointers(in this cases, MapView pointer).
The singleton is good, but unless you are going to use CLLocationManager from multiple places, like more than 3~4 places, it's not that necessary, I think
Hope I didn't confuse you. Based on what you posted, it seems like this way can be simple solution for your goal. If I didn't catch your true intention, please let me know.
I am not sure this is the best way, but I've been setting up my main controller (the one that is loaded first) as a location manager delegate. When the location updates it fires off a notification with the new location as the notification object. Any controllers listening can then use that data however they need it.
As an aside, Apple's LocateMe app instantiates the location manager three times. So, by their example, having multiple LocationManagers might not be a problem.
From what I've read, best practice for this is to add CLLocationManager to your App Delegate as you can access it from any view.
Short sample code to put in your view where you need the CLLocationManager
....imports....
#implementation YourViewController
- (void)viewDidLoad {
self.myLocationManager = [[UIApplication sharedApplication] delegate].yourLocationManagerVarName;
}
#end
Hop that helps.
Maybe you should consider a MVC oriented approach. From your description your are missing a model layer representation of your user. Defining a simple User class with a basic CLLocation property would be a first step.
#interface User {}
#property (nonatomic, retain) CLLocation *location;
#end
#implementation User
#synthesize location;
- (void)dealloc {
self.location = nil;
[super dealloc];
}
#end
The same instance of the User will be passed to your view controller. It may be created in the app delegate.
Next create location services object for your app. It will start the CLLocationManager, and give the location to your user. You may have to set the GPS accuracy, ignore frames you don't want, and implement basic LBS logic here.
At this point, you have a feature full app, without any UI. This is a good design in the way it can be reused and tested.
Now stack your UI on top of that. Give your root controller a pointer to the User instance in your app delegate. Your view controller pass this pointer to modals / navigations view controllers it creates.
This controller start observing User's location changes in their viewDidLoad and react accordingly.
- (void)viewDidLoad {
[self observeValueForKeyPath:#"location" ofObject:self.user change:0 context:NULL];
}
Your view controller would also register for notification raised by your location services objects to display an alert to the user.
Based on other answers:
there is no real penalty to create multiple CLLocationManager instances in your code. The only side effect is that the api is asynchronous, thus you have to wait to get a valid location in your view controller. You can try to get the current location from the location manager on your viewDidLoad using locationManager.location API.
don't share stuff from your app delegate. This prevent code reuse. What if you reuse your views and you app delegate don't have a location manager ?
if you need more code, please ask.