I am new to objective-c and have been trying to work on this problem for the past 2 and a half hours. So far I was able to have some success and my app can start searching for the user's location as soon as the app is launched.
I then setup a delegate for CLLocationManager based off of this advice:
"Add the syntax to declare that this class (the ViewController.h/m) is adopting this particular protocol. By default, any other classes that create an instance of this class will also automatically adopt the protocol.
#interface ViewController : UIViewController <CLLocationManagerDelegate>
The line of code listed above shows the syntax used to show ViewController adopts the CLLocationManagerDelegate protocol. We simply add it between angled brackets <> in the #interface line in the header."
So I successfully added the above line inside my ViewController.m and ViewController.h file, and then I also followed that same tutorials advice of:
"The full method is:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
and when implemented it tells the delegate that new location data is available. We have two arguments on this method. The first lets you know which CLLocationManager provided the update and the last provides the CLLocation information which is stored in an NSArray."
Below is all of my ViewController.h code and then next wll be my ViewController.m code:
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#interface ViewController : UIViewController <CLLocationManagerDelegate>
#property (nonatomic, strong) IBOutlet UILabel *gpsLabel;
-(IBAction)gpsButton;
#end
Here is my ViewController.m code:
#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
#interface ViewController () <CLLocationManagerDelegate>
#property (nonatomic, strong) CLLocationManager * gpsLM;
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.gpsLM = [[CLLocationManager alloc]init];
[self.gpsLM startUpdatingLocation];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(IBAction)gpsButton{
}
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
}
#end
I am confused on where to go from here. Assuming I am even doing everything correctly so far(am I?), then how do I access the location data that has been stored in the NSArray object called locations?
Thank you for the help.
Welcome to the iOS community!
First, you'll probably find it helpful to look at the CLLocationManagerDelegate reference, under the heading for the locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations method. The important bit here is:
locations: An array of CLLocation objects containing the location data.
This array always contains at least one object representing the
current location. If updates were deferred or if multiple locations
arrived before they could be delivered, the array may contain
additional entries. The objects in the array are organized in the
order in which they occurred. Therefore, the most recent location
update is at the end of the array.
The important thing to get is that this method is called by the system when the user's location changes.
So, for instance, you could print out a message to your console when the location updates by changing your implementation to something like:
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
CLLocation *currentLocation = [locations lastObject];
NSLog(#"Location is: %.5f %.5f", currentLocation.coordinate.latitude, currentLocation.coordinate.longitude);
}
If you instead want to do something in response to user activity, you could use manager.location, which is automatically updated every time the CLLocationManager detects a new location. (If this didn't exist, you would need to add another instance variable to your class to store the most recent location, and update that in locationManager:didUpdateLocations:.)
So, for instance, if you wanted to update your label with the current location whenever the button was pressed, you could add something like:
-(IBAction)gpsButton {
CLLocation *currentLocation = self.gpsLM.location;
self.gpsLabel.text = [NSString stringWithFormat:#"Location is: %.5f, %.5f"];
}
(Note: this assumes that the gpsButton action and the gpsLabel outlet are hooked up to something graphically in Interface Builder.)
If you're familiar with other programming languages, this is a push versus pull distinction. CLLocationManager provides a push model (it calls your locationManager:didUpdateLocations: method to inform you immediately of changes), and also a pull model (you can ask it for the most current location through .location at any time). Which one you use will depend on what you want to do.
Related
I found this good tutorial for develop an app with iBeacon:
http://www.appcoda.com/ios7-programming-ibeacons-tutorial/
But with this implementation, as the author say, if you start the receiver when it's already in the beacon range, it's not going to fire. If you want find an ibeacon you need to walk far away from its region, and then walk back into range.
How can I modify this code to find a beacon that it is in range when I lunch the app?
I use Xcode6, IPad air, IOS 8
This is the simplified code from the tutorial:
in ViewController.h
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#interface ViewController : UIViewController<CLLocationManagerDelegate>
#property (strong, nonatomic) CLBeaconRegion *myBeaconRegion;
#property (strong, nonatomic) CLLocationManager *locationManager;
#end
In ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:#"ACFD065E-C3C0-11E3-9BBE-1A514932AC01"];
self.myBeaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid
identifier:#"com.appcoda.testregion"];
[self.locationManager startMonitoringForRegion:self.myBeaconRegion];
}
- (void)locationManager:(CLLocationManager*)manager didEnterRegion:(CLRegion *)region
{
NSLog(#"Finding beacons.");
[self.locationManager startRangingBeaconsInRegion:self.myBeaconRegion];
}
-(void)locationManager:(CLLocationManager*)manager didExitRegion:(CLRegion *)region
{
NSLog(#"None found.");
[self.locationManager stopRangingBeaconsInRegion:self.myBeaconRegion];
}
-(void)locationManager:(CLLocationManager*)manager
didRangeBeacons:(NSArray*)beacons
inRegion:(CLBeaconRegion*)region
{
NSLog(#"Beacon found");
}
The didEnterRegion and didExitRegion callbacks only fire when the user crosses the region boundary. In the case of an iBeacon this means moving from "Inside" to "Outside" or vice versa.
When you fire up the app and start monitoring for your beacon region, you can request the current state for your beacon region to determine if your user is inside or outside.
Implement the didDetermineState callback:
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
This callback gets triggered after you start monitoring your region, anytime a region boundary is crossed (so be careful you don't duplicate logic here and inside didEnter/ExitRegion), and in response to a call to requestStateForRegion:
Hope this helps... if you need more -> https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManagerDelegate_Protocol/index.html#//apple_ref/occ/intfm/CLLocationManagerDelegate/locationManager:didDetermineState:forRegion:
I cannot get even the simplest location updating code to work. I have a location app that worked fine for over a year, and now when I try to compile and run it (after upgrading to xcode 6.1 and with no changes to the code), after calling startUpdatingLocation, the didUpdateLocations callback never fires, and I can see that the gps indicator never appears next to the battery indicator as it should.
I have started a new test project that does nothing but attempts to register for location updates, and still the same results: no location updates on device or simulator.
Here is the code for the single view controller of the test project:
//ViewController.h
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#interface ViewController : UIViewController <CLLocationManagerDelegate>
{
}
#property (strong, nonatomic) CLLocationManager* locationManager;
#end
//ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.locationManager = [[CLLocationManager alloc]init];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[self.locationManager requestWhenInUseAuthorization];
[self.locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
NSLog(#"got a location");
}
-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(#"Error: %#",error.description);
}
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
NSLog(#"got a location");
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
#end
It would be fantastic if someone with the latest version of xcode could create a project like this that does nothing but receive location updates, verify that it works, and post the code in full. I have tried adding things to the plist file and all other remedies from similar questions to no avail.
Yes, a few things have changed in the new version and it can be a headache. I was having the same problem and this is the thread that solved it for me here.
You need to add these lines info.plist:
<key>NSLocationWhenInUseUsageDescription</key>
<string>The spirit of stack overflow is coders helping coders</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>I have learned more on stack overflow than anything else</string>
And make sure that you call [CLLocationManager requestWhenInUseAuthorization] or [CLLocationManager requestAlwaysAuthorization] before you try to update the users location.
I am developing an iOS 7 app that uses location. I never get the location updated. If I do the same with startUpdatingLocation it works good. I have read the documentation and I don't know where is the problem.
The location is enabled for the app, and it ask you if you want to enabled it the first time I run the app. Also the location icon in the status bar is not showed.
Update: In the simulator it works, it gives me an updated location. Why it doesn't work in the device?
It's only going to be used in a class, here is the configuration.
#import <CoreLocation/CoreLocation.h>
#interface ClassLocation : UIViewController <CLLocationManagerDelegate>{
}
Setting the property in the .h and in the .m the synthesize
#property (strong, nonatomic) CLLocationManager *localizacionManager;
#synthesize localizacionManager;
ViewDidLoad:
localizacionManager=[[CLLocationManager alloc] init];
[localizacionManager setDelegate:self];
[localizacionManager setDesiredAccuracy:kCLLocationAccuracyKilometer];
[localizacionManager startMonitoringSignificantLocationChanges];
And the two delegate methods:
-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error{
NSLog(#"error: %#",error.description);
}
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
NSLog(#"Location updated");
}
Add a key to your Info.plist and request authorization from the location manager asking it to start.
NSLocationWhenInUseUsageDescription
NSLocationAlwaysUsageDescription
You need to request authorization for the corresponding location method.
[self.locationManager requestWhenInUseAuthorization]
[self.locationManager requestAlwaysAuthorization]
The same happens to me in iOS 7. I presume it's due to the last location being cached; if the current location has no significant changes compared to the last one, the delegate never gets called.
You can access the last cached location by using the "location" property of the CLLocationManager :
#property (readonly, nonatomic, copy) CLLocation *location ;
Hope this helps.
My app display the user's last known/current coordinates in a text label when a button is pressed.
I set up a block of code in my main file to print the user's latitude coordinate to the log, but it does not print anything to the log when I run the app.
Why won't the NSLog print to the console?
Here is the snippet of code that is supposed to be printing the location to the log once the app launches and the user allows the app to access their location:
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
CLLocation * currentLocation = [locations lastObject];
NSLog(#"%f", currentLocation.coordinate.latitude);
Below is my full ViewController.h code:
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#interface ViewController : UIViewController <CLLocationManagerDelegate>
#property (nonatomic, strong) IBOutlet UILabel * gpsLabel;
#property (nonatomic, strong) CLLocationManager * gpsLM;
-(IBAction)gpsButton;
#end
And here is my full ViewController.m code:
#import "ViewController.h" //This imports the all of the code we have typed in the ViewController.h file.
#import <CoreLocation/CoreLocation.h> //This imports the CoreLocation framework needed for location apps.
//This assigns the Location Manager's delegate to this view controller
#interface ViewController () <CLLocationManagerDelegate>
//This tells the delegate that new location data is available. Manager is the object that updates the event, and the locations object is where the array of location data is stored.
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//This allocates memory for and initializes the gpsLM object we setup in ViewController.h
//This means that we can now use the object and do things with it.
self.gpsLM = [[CLLocationManager alloc]init];
//This calls a startUpdatingLocation method for our CLLocationManager object called gpsLM.
//Because this is all in viewDidLoad, it all gets executed right away as soon as the app is opened.
[self.gpsLM startUpdatingLocation];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//This executes the instance method that we declared above in the header.
//Now we are actually implementing the method and can tell it what we want it to do.
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
//This creates an object called currentLocation and sets it's value to whatever the last value is in the locations array.
//Notice how it is also calling a method of lastObject for the object called locations.
//So remember that you can set variables and objects equal to the result of a method call.
CLLocation * currentLocation = [locations lastObject];
//This prints out text to the debug console that states the latitude coordinate of the user's iPhone.
NSLog(#"%f", currentLocation.coordinate.latitude);
}
-(IBAction)gpsButton{
CLLocation * currentLocation = self.gpsLM.location;
self.gpsLabel.text = [NSString stringWithFormat:#"Your Location is %#", currentLocation];
}
#end
It seems that you forgot to assign the location manager delegate:
self.gpsLM = [[CLLocationManager alloc]init];
self.gpsLM.delegate = self; // <-- ADD THIS
[self.gpsLM startUpdatingLocation];
Without this assignment, the location manager doesn't know what object to give the location update to. The method locationManager:didUpdateLocations: never runs.
I'm having trouble getting my delegate method to fire. I have the following code:
#class Location;
#protocol LocationDelegate
- (void)location:(Location*)location foundLocation:(CLLocation*)location;
- (void)location:(Location*)location failedToLocateUserWithError:(NSError*)error;
#end
#interface Location : NSObject {
__unsafe_unretained id <LocationDelegate> delegate;
}
#property (nonatomic, assign) id <LocationDelegate> delegate;
#end
...
#implementation Location
#synthesize delegate;
- (void)startUpdatingLocation {
NSLog(#"%#", delegate); // prints '(null)'
...
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
NSLog(#"%#", delegate); // prints '(null)'
[delegate location:self foundLocation:newLocation];
}
#end
Used to work fine for other projects, so wondering if it has to do with ARC? The object that allocates Location does conform to LocationDelegate. I am also setting the delegate to self. But the delegate method just isn't firing, and if I output it, it's null.
Thanks
From just the code you've pasted here, I don't see anything that is holding on to your delegate (a retained reference).
Everything you've pasted specifically shows you going out of your way to not have the delegate retained by this code.
If thats how you want it to be, then you better be sure it's been retained elsewhere - otherwise ARC will correctly conclude that since no one has a strong (retained) reference to the delegate, that it's safe (and proper) to release it.
The object that allocates Location does conform to LocationDelegate. I
am also setting the delegate to self.
And who has a strong reference to that object?
Wherever you are allocating Location have you assigned the delegate?
Eg:
Location *location = [[Location alloc] init];
location.delegate = self;