I want to receive location updates. I have added a location delegate to the header file but didUpdateToLocation method not fire my code is
#interface FirstViewController : UIViewController <CLLocationManagerDelegate>
{
UILabel *myLabel;
CLLocationManager *manager;
CLGeocoder *geocoder;
CLPlacemark *placemark;
}
-(void) showCurrentLocation;
#end
and .m file like this:
#import "FirstViewController.h"
#interface FirstViewController ()
#end
#implementation FirstViewController
-(id) init
{
self=[super init];
if (self) {
self.view.backgroundColor=[UIColor purpleColor];
self.edgesForExtendedLayout=UIRectEdgeNone;
[self showCurrentLocation];
}
return self;
}
-(void) showCurrentLocation{
manager=[[CLLocationManager alloc] init];
manager.pausesLocationUpdatesAutomatically=NO;
geocoder=[[CLGeocoder alloc] init];
manager.delegate=self;
manager.desiredAccuracy=kCLLocationAccuracyBest;
}
-(void) locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(#"Error %#",error);
NSLog(#"Faild to get location");
}
-(void) locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
NSLog(#"Location %#",newLocation);
CLLocation *currentlocation=newLocation;
if (currentlocation!=nil)
{
myLabel=[[UILabel alloc] initWithFrame:CGRectMake(30, 30, 200, 200)];
[myLabel setText:[NSString stringWithFormat:#"Latit %8f",currentlocation.coordinate.latitude]];
myLabel.textColor=[UIColor whiteColor];
[self.view addSubview:myLabel];
[geocoder reverseGeocodeLocation:currentlocation completionHandler:^(NSArray *placemark1 ,NSError *erroe){
if (erroe==nil && [placemark1 count]>0)
{
NSLog(#"Location");
}
else
{
NSLog(#"error 2 %#",erroe.debugDescription);
}
}];
}
}
You need to start location manager
manager = [CLLocationManager new];
manager.delegate = self;
[manager startUpdatingLocation]; // forgotten in the code above
Also be sure that you setup your scheme properly to simulate location in test mode via Simulator
Related
I've been unable to figure out why location manager isn't providing updates to me. I've added the key, tested on simulator and on a device.
Edit: STDOUT also contains "Delegate: "
STDOUT
2014-11-28 22:18:30.066 Speedo[14091:150298] Status: 4
2014-11-28 22:18:30.069 Speedo[14091:150298] Is when in use? 1
2014-11-28 22:18:30.158 Speedo[14091:150298] New status: 4
2014-11-28 22:18:30.159 Speedo[14091:150298] Is when in use? 1
FirstViewController.h
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#interface FirstViewController : UIViewController <CLLocationManagerDelegate>
#property (weak, nonatomic) IBOutlet UILabel* speedLabel;
#end
FirstViewController.cpp
#import "FirstViewController.h"
#import <CoreLocation/CoreLocation.h>
#interface FirstViewController ()
#end
#implementation FirstViewController {
CLLocationManager *locationManager;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
self.speedLabel.text = #"";
if([locationManager respondsToSelector: #selector(requestWhenInUseAuthorization)]) {
[locationManager requestWhenInUseAuthorization];
}
// locationManager.desiredAccuracy = kCLLocationAccuracyBest;
// locationManager.distanceFilter = 0.01;
[locationManager startUpdatingLocation];
NSLog(#"Delegate: %#", locationManager.delegate);
NSLog(#"Status: %d", [CLLocationManager authorizationStatus]);
NSLog(#"Is when in use? %d", [CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorizedWhenInUse);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(#"didFailWithError: %#", error);
UIAlertView *errorAlert = [[UIAlertView alloc]
initWithTitle:#"Error" message:#"Failed to Get Your Location" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[errorAlert show];
}
-(void) locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
NSLog(#"New status: %d", status);
NSLog(#"Is when in use? %d", [CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorizedWhenInUse);
[manager startUpdatingLocation];
}
-(void)locationManage: (CLLocationManager *)manager didUpdateLocations:(NSArray*)locations {
CLLocation* last = [locations lastObject];
NSLog(#"Speed: %f m/s", last.speed); // meter per second
// TODO: support mutli-units
// m/s -> mph
// 1 -> 2.236
self.speedLabel.text = [NSString stringWithFormat:#"%d", (int)(last.speed * 2.23694)];
}
#end
Perhaps it's just the result of a typo.
-(void)locationManage: (CLLocationManager *)manager didUpdateLocations:(NSArray*)locations {
should be
-(void)locationManager: (CLLocationManager *)manager didUpdateLocations:(NSArray*)locations {
You forgot the "r" at the end of "locationManager".
I'm trying to use core location in iOS 8 simulator, for that I added in my view an object of type 'MapKit View', In tab Atributtes inspector is checked the option show user location, In my project I'm using ARC, below is the structure of my code:
ViewController.h
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>
#interface MeuPrimeiroViewController : UIViewController <MKMapViewDelegate, CLLocationManagerDelegate>{
IBOutlet MKMapView *mapView;
}
#property (nonatomic, strong) CLLocationManager *locationManager;
#end
ViewController.m
#synthesize locationManager;
- (void)viewDidLoad {
[super viewDidLoad];
if ([CLLocationManager locationServicesEnabled]) {
NSLog(#"CLLocationManager locationServicesEnabled == ON");
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
// Check for iOS 8 Vs earlier version like iOS7.Otherwise code will
// crash on ios 7
if ([locationManager respondsToSelector:#selector
(requestWhenInUseAuthorization)]) {
[locationManager requestAlwaysAuthorization];
}
[locationManager startUpdatingLocation];
}else{
NSLog(#"CLLocationManager locationServicesEnabled == OFF");
}
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{
NSLog(#"It works this method is called");
}
-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error{
NSLog(#"Error: %#",[error description]);
}
In my info.plist file I add this key (NSLocationAlwaysUsageDescription) with this value (String).
If I go to attributes inspector and enable the checkbox (shows user location) I receive this error message, and core location methods are not called:
Trying to start MapKit location updates without prompting for location authorization. Must call -[CLLocationManager requestWhenInUseAuthorization] or -[CLLocationManager requestAlwaysAuthorization] first.
If I disable the checkbox, this message disappear, and core location methods not called again. I try to change the navigation to londom, try to change location to free run but nothing I tried worked, the methods do not continue to be called and never showed an empowering message to use core location. I believe I've already tried everything, anyone have any suggestions or a solution to this problem?
This is what i am doing for my app and working perfectly
- (void)startSignificantChangeUpdates {
if (nil == self.locationManager) {
self.locationManager = [[CLLocationManager alloc] init];
}
self.locationManager.delegate = self;
[self.locationManager startMonitoringSignificantLocationChanges];
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations {
CLLocation* location = [locations lastObject];
if (location) {
self.currentLocation = location;
NSString *latitude = [NSString stringWithFormat:#"%f", location.coordinate.latitude];
NSString *longitude = [NSString stringWithFormat:#"%f", location.coordinate.longitude];
self.latLong = [NSString stringWithFormat:#"%#,%#",latitude, longitude];
}
if (!self.geocoder)
self.geocoder = [[CLGeocoder alloc] init];
[self.geocoder reverseGeocodeLocation:location completionHandler:
^(NSArray* placemarks, NSError* error){
if ([placemarks count] > 0) {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
if (placemark.postalCode) {
self.currentZipCode = placemark.postalCode;
}
[[NSNotificationCenter defaultCenter] postNotificationName:#"zipCodeFoundNotification" object:self.currentZipCode userInfo:nil];
} else {
[[NSNotificationCenter defaultCenter] postNotificationName:#"zipCodeFoundNotification" object:nil userInfo:nil];
}
}];
}
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
if (status != kCLAuthorizationStatusAuthorized && status != kCLAuthorizationStatusNotDetermined) {
if (status == kCLAuthorizationStatusDenied){
self.currentZipCode = #"kCLAuthorizationStatusDenied";
} else if (status == kCLAuthorizationStatusRestricted) {
self.currentZipCode = #"kCLAuthorizationStatusRestricted";
}
}
}
The goal I want to achieve:
I want to display the user address in a view. The view is taken care of by a view controller . This VC is using the GeoAPI to get access to the user location. The GeoAPI is wrapped around the LocationManager-Class which in turn is taking care of all the locationmanager stuff. In this location Manager class I would like to reversegeocode as mentioned above.
The geoAPI has some convenience methods that can be used to get the information I want.
GeoAPI:
#import <Foundation/Foundation.h>
#interface MFGeoAPI : NSObject
#property (nonatomic, strong) NSDictionary *userLocationInfoDict;
+ (MFGeoAPI *)sharedInstance;
- (CLLocation *)getCurrentUserLocation;
#end
#interface MFGeoAPI () {
LocationManager *locationManager;
MotionManager *motionManager;
}
#end
#implementation MFGeoAPI
+(MFGeoAPI *)sharedInstance {
static MFGeoAPI *_sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[MFGeoAPI alloc] init];
});
return _sharedInstance;
}
- (id)init {
if (self = [super init]) {
locationManager = [[LocationManager alloc] initLocationManager];
motionManager = [[MotionManager alloc] initMotionManager];
}
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(getUserLocationInformation:) name:#"UserLocationInfo" object:nil];
return self;
}
- (CLLocation *)getCurrentUserLocation {
return [locationManager getCurrentUserLocation];
}
- (void)getUserLocationInformation:(NSNotification *)notification {
NSLog(#"OK");
self.userLocationInfoDict = [[NSDictionary alloc] initWithDictionary:notification.userInfo];
}
#end
Now.. due to the fact of the async character of the reverse geocode function I won't be sure when the dictionary with the user address will be available.
The Location Manager class contains the following:
Location Manager:
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#interface LocationManager : NSObject <CLLocationManagerDelegate>
#property (nonatomic, strong) CLLocationManager *locationManager;
#property (nonatomic, strong) CLLocation *userLocation;
#property (nonatomic, strong) NSDictionary *userLocDict;
- (id)initLocationManager;
- (CLLocation *)getCurrentUserLocation;
#end
#import "LocationManager.h"
#implementation LocationManager
- (id)initLocationManager {
if (self = [super init]) {
if (self.locationManager == nil) {
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
if (![self.locationManager locationServicesEnabled]) {
UIAlertView *servicesDisabledAlert = [[UIAlertView alloc] initWithTitle:#"Location Services Disabled" message:#"You currently have all location services for this device disabled. If you proceed, you will be asked to confirm whether location services should be reenabled." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[servicesDisabledAlert show];
}
}
[self startUpdatingLocation];
[self startFetchingCurrentUserLocationInformation];
}
return self;
}
- (void)startUpdatingLocation {
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.headingFilter = kCLHeadingFilterNone;
[self.locationManager startUpdatingHeading];
[self.locationManager startUpdatingLocation];
self.userLocation = self.locationManager.location;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
NSLog(#"FAILED");
}
- (CLLocation *)getCurrentUserLocation {
return self.userLocation;
}
- (void)startFetchingCurrentUserLocationInformation {
[self delayedReverseGeocodeLocation];
}
- (void)delayedReverseGeocodeLocation {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[self reverseGeocodeLocation];
}
- (void)reverseGeocodeLocation {
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:self.userLocation completionHandler:^(NSArray *placemarks, NSError *error) {
NSDictionary *dict = [[placemarks firstObject] addressDictionary];
if (placemarks.count > 0) {
NSDictionary *info = [NSDictionary dictionaryWithObject:dict forKey:#"transferDict"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"UserLocationInfo" object:self userInfo:info];
}
}];
}
In the view Controller I would like to get access to the userLocation Information Dictionary that I am passing through the API
View Controller
- (id)init {
if (self = [super init]) {
self.userLocation = [[MFGeoAPI sharedInstance] getCurrentUserLocation];
NSLog(#"%#", [[MFGeoAPI sharedInstance].userLocationInfoDict description]);
}
return self;
}
But the userlocationInfoDict is empty. I guess this is due to the fact that my function-call "startFetchingCurrentUserLocationInformation" is returning before the async block returns the values I need. Then I thought by taking care of this through NSNotificationCenter I will be informed when the data is available. But now I am stuck:
What do I have to do to get access to the userLocationInformation passed by the GeoAPI to do it in time when the data has arrived?
Any help appreciated!
Thank you
Sebastian
I've heard time and time again that there is always a better pattern than the singleton, but I can't understand how else my application could access the device location without waiting for the GPS to return data (I'm assuming that the location system is only running when explicitly called for, correct me if wrong).
So is there a better pattern for accessing CLLocation data from multiple (unrelated) controllers? Or can I expect the device location to be updating in the background even if I am not accessing it through a CLLocationManager?
Declare a single class . Like the following .
MyLocation.h
#protocol MyCLControllerDelegate <NSObject>
- (void)locationUpdate:(CLLocation *)location;
- (void)locationError:(NSError *)error;
#end
#interface MyLocation : NSObject <CLLocationManagerDelegate> {
CLLocationManager *locationManager;
id delegate;
}
#property (nonatomic, strong) CLLocationManager *locationManager;
#property (nonatomic, strong) id <MyCLControllerDelegate> delegate;
MyLocation.m
#import "MyLocation.h"
#implementation MyLocation
#synthesize locationManager;
#synthesize delegate;
- (id) init {
self = [super init];
if (self != nil) {
if([CLLocationManager locationServicesEnabled]) {
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied || [CLLocationManager authorizationStatus] == kCLAuthorizationStatusRestricted )
{
[self showAlertWithTitle:#"Warning" andWithMessage:#"Determining your current location cannot be performed at this time because location services are enabled but restricted" forTargetView:self];
NSlog(#"Determining your current location cannot be performed at this time because location services are enabled but restricted");
}
else
{
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self; // send loc updates to myself
[self.locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[self.locationManager setDistanceFilter:kThresholdDistance];
[self.locationManager startUpdatingLocation];
NSLog(#"Location sharing set ON!");
}
} else {
[MobileYakHelper showAlertWithTitle:#"Error" andWithMessage:#"Determining your current location cannot be performed at this time because location services are not enabled." forTargetView:self];
NSLog(#"Location sharing set OFF!");
}
}
return self;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
NSDictionary *dictValue = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithDouble:newLocation.coordinate.latitude], #"latitude",
[NSNumber numberWithDouble:newLocation.coordinate.longitude], #"longitude",
nil];
[[NSUserDefaults standardUserDefaults] setValue:dictValue forKey:#"MY_LOCATION"];
CLLocationDistance meters = [newLocation distanceFromLocation:oldLocation];
if (meters >= kThresholdDistance ) {
[self.delegate locationUpdate:newLocation];
}
}
- (void)locationManager:(CLLocationManager *)manager
didFailWithError:(NSError *)error
{
[self.delegate locationError:error];
}
#end
To use it in few controller .Adapt delegate in it's .h file and use like as follows :
- (void) initializeLocations
{
MyLocation _myLocation = [[MyLocation alloc] init];
_myLocation.delegate = self;
_myLocation.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
CLLocation * currentLocation = _myLocation.locationManager.location;
// Updating user's current location to server
[self sendUserCurrentCoordinate:currentLocation.coordinate];
// start updating current location
_myLocation.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[_myLocation.locationManager startUpdatingLocation];
[_myLocation.locationManager startUpdatingLocation];
}
- (void)locationUpdate:(CLLocation *)location {
NSLog(#"location %#", location);
}
- (void)locationError:(NSError *)error {
NSLog(#"locationdescription %#", [error description]);
}
I have a custom navbar with a locateButton that is visible in every view
When a user taps the locateButton, I would like to update lat and long on every view. I've got it working in one view at a time, with the following code.
ViewController.m
- (UIBarButtonItem *)locateButton {
[locationButton addTarget:self action:#selector(locationPressed:) forControlEvents:UIControlEventTouchUpInside];
}
- (IBAction)locationPressed:(id)sender {
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
}
#pragma mark - CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(#"didFailWithError: %#", error);
UIAlertView *errorAlert = [[UIAlertView alloc]
initWithTitle:#"Error" message:#"Failed to Get Your Location" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[errorAlert show];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
NSLog(#"didUpdateToLocation: %#", newLocation);
CLLocation *currentLocation = newLocation;
if (currentLocation != nil) {
latLabel.text = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.longitude];
longLabel.text = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.latitude];
}
// Stop Location Manager
[locationManager stopUpdatingLocation];
// Reverse Geocoding
NSLog(#"Resolving the Address");
[geocoder reverseGeocodeLocation:currentLocation completionHandler:^(NSArray *placemarks, NSError *error) {
NSLog(#"Found placemarks: %#, error: %#", placemarks, error);
if (error == nil && [placemarks count] > 0) {
placemark = [placemarks lastObject];
addressLabel.text = [NSString stringWithFormat:#"%#, %#",
placemark.locality,
placemark.administrativeArea];
addressLabel.numberOfLines = 0;
} else {
NSLog(#"%#", error.debugDescription);
}
} ];
I'm trying to add a singleton to accomplish this.
In Location.h
// Location.h
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>
#interface Location : NSObject <CLLocationManagerDelegate>
#property (nonatomic, strong) CLLocationManager* locationManager;
+ (Location*)sharedSingleton;
#end
Then in Location.M
// Location.m
#import "Location.h"
#implementation Location
#synthesize locationManager;
- (id)init {
self = [super init];
if(self) {
self.locationManager = [CLLocationManager new];
[self.locationManager setDelegate:self];
[self.locationManager setDistanceFilter:kCLDistanceFilterNone];
[self.locationManager setHeadingFilter:kCLHeadingFilterNone];
[self.locationManager startUpdatingLocation];
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
//do any more customization to your location manager
}
return self;
}
+ (Location*)sharedSingleton {
static Location* sharedSingleton;
if(!sharedSingleton) {
#synchronized(sharedSingleton) {
sharedSingleton = [Location new];
}
}
return sharedSingleton;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
NSLog(#"didUpdateToLocation: %#", newLocation);
[locationManager stopUpdatingLocation];
}
#end
How do I get my - (IBAction)locationPressed:(id)sender in another view let's say HomeViewController.m to call this locationManager method and also update the singleton value?
You might consider a singleton as your location manager and location manager delegate. There are a bunch of stackoverflow questions about creating singletons like this one: Singleton in iOS 5?.
Then when your user clicks that button, your singleton class will be called and updated and other views will look at that same singleton to get their location info.