I started using Xcode a couple days ago and I'm completely lost. I'm trying to get a GPS locator app running found here.
Basically, the app prints any updated GPS information using NSLog, which as far as my understanding goes, prints to Xcode's console. However, I'd like to get this info printed onto the screen.
Here's the code from CFAStartViewController.m that successfully prints to the screen:
-(void)viewDidAppear:(BOOL)animated{
CFAAppDelegate *appDelegate=(CFAAppDelegate *)[UIApplication sharedApplication].delegate;
CLLocation *currentLocation=appDelegate.locationManager.location;
self.labelLocationInformation.text=[NSString stringWithFormat:#"latitude: %+.6f\nlongitude: %+.6f\naccuracy: %f",
currentLocation.coordinate.latitude,
currentLocation.coordinate.longitude,
currentLocation.horizontalAccuracy];
}
And here's the code in CFAAppDelegate.m that successfully prints to the console:
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{
NSDate* eventDate = newLocation.timestamp;
NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
if (abs(howRecent) < 15.0)
{
//Location timestamp is within the last 15.0 seconds, let's use it!
if(newLocation.horizontalAccuracy<35.0){
//Location seems pretty accurate, let's use it!
NSLog(#"latitude %+.6f, longitude %+.6f\n",
newLocation.coordinate.latitude,
newLocation.coordinate.longitude);
NSLog(#"Horizontal Accuracy:%f", newLocation.horizontalAccuracy);
//Optional: turn off location services once we've gotten a good location
//[manager stopUpdatingLocation];
}
}
}
I tried changing the calls to NSLog to self.labelLocationInformation.text, with a similar format to that of CFAStartViewController, but it doesn't seem to do anything. I've read through the basic tutorials of Xcode, but I feel there's some knowledge lacking (obviously) on what to do overall.
If the best you could do is post a link that helps me solve this problem, that would be great.
I think the problem is due to scope - the didUpdateToLocation: method is sent to the location manager delegate, ie your app delegate, which doesn't have the label in scope. Try changing your delegate for the location manager to be your CFAStartViewController, and move the associated location manager delegate code from App Delegate to CFAStartViewController. Then the label will be in scope when didUpdateToLocation: is called.
Based on your clarifying response to my comment.... If you want the data from CFAAppDelegate to be presented in your CFAStartViewController then you need to send that data to CFAStartViewController (or CFAStartViewController needs to get it). To send it 1) provide a storyboard id for CFAStartViewController such as "CFAVC". 2) define or expose properties in CFAStartViewController to contain your data. 3) instantiate CFAStartViewController via the following from within CFAAppDelegate:
// in CFAStartViewController.h define property such as:
#property (strong, nonatomic) CLLocation *incomingLocation;
// in CFAAppDelegate.m
// get a pointer to your VC
CFAStartViewController *destinationVC = [self.storyboard instantiateViewControllerWithIdentifier:#"CFAVC"];
// set properties on VC
destinationVC.incomingLocation = newLocation;
// show CFAStartViewController (or use whatever method you are currently using)
[self.navigationController pushViewController:destinationVC animated:YES];
Related
i am using here maps in my app and works fine. i pass destination coordinate from one view controller to another viewcontroller which contains here map and its methods. i tried to get the current location inside the method in the second view controller to calculate the roue, but current location always returns 0,0.please advice how to get current location inside the method where i pass the values from first viewcontroller
viewcontroller1:
- (IBAction)btn_navigate:(id)sender
{
NSLog(#"%#",_POiarray);
_VC=[[NaviMeVC alloc]init];
[_VC GetPoi:_POiarray];
[self.navigationController pushViewController:_VC animated:YES];
}
mapviewcontroller:
-(void)GetPoi:(NSMutableArray *)anArray
{
//get current location
NMAGeoPosition * position=[[NMAGeoPosition alloc]init];
position = [[NMAPositioningManager sharedPositioningManager] currentPosition];
_StartCoordinate=[[NMAGeoCoordinates alloc]initWithLatitude:position.coordinates.latitude longitude:position.coordinates.longitude];
//not working returns nil
[[NMAMapLoader sharedMapLoader] setDelegate:self];
_SelectedPOi=[[NSMutableArray alloc]init];
_SelectedPOi=anArray;
NSString *lat=[anArray valueForKey:#"latitude"];
CLLocationDegrees latitu=[lat doubleValue];
NSString *longi=[anArray valueForKey:#"longitude"];
CLLocationDegrees longit=[longi doubleValue];
_DestinationCoordinate=[[NMAGeoCoordinates alloc]initWithLatitude:latitu longitude:longit];
NSLog(#"%f %f",_DestinationCoordinate.latitude,_DestinationCoordinate.longitude);
NSNumber *lati = [NSNumber numberWithDouble:latitu];
NSNumber *longitu = [NSNumber numberWithDouble:longit];
NSDictionary *userLocation=#{#"lat":lati,#"long":longitu};
NMARoutingMode* routingMode = [[NMARoutingMode alloc] initWithRoutingType:NMARoutingTypeShortest
transportMode:NMATransportModeCar
routingOptions:0];
[self CalculateRoute:routingMode];
}
To start receiving positioning updates you need to call NMAPositioningManager startPositioning which I don't see in your sample code. More details in user guide link below. Please read the other instructions on that page to see if anything helps.
Another thing to check: have you added NSLocationWhenInUseUsageDescription into your projects Info.plist file to ensure your app can receive user location from CLLocationManager?
Also, this may seem obvious but please keep in mind that you will only be able to receive a valid value for currentPosition if you have a position fix.
Positioning User Guide
NMAPositioningManager Doxygen
Hey so I have a map view that zooms in on the users location upon view load. Currently I find the users location using the CLLocationManager and initialize the map in the didUpdateLocations delegate method. However, as this is called multiple times I use a global BOOL that is set to true after the map is set once and then never set it again. Here is the code:
Define global BOOL and set it to false in the viewDidLoad:
#implementation MainMapViewController
{
BOOL hasInitializedMap;
}
- (void)viewDidLoad
{
[super viewDidLoad];
hasInitializedMap = NO;
}
I then get the user's location and set the maps zoom inside the (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations delegate method:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *location = [locations lastObject];
[self initializeMainMapView:location.coordinate.longitude latitude:location.coordinate.latitude];
}
- (void)initializeMainMapView:(float)longitude latitude:(float)latitude
{
if (!hasInitializedMap)
{
CLLocationCoordinate2D zoomLocation;
zoomLocation.latitude = latitude;
zoomLocation.longitude = longitude;
MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(zoomLocation, 1500, 1500);
[mainMapView setRegion:viewRegion animated:YES];
hasInitializedMap = YES;
}
}
This code works but has left me wondering if there is a better way to do this. I hate using global flags like this as it can make your code confusing and is sloppy at best. Is there a better way to do what I am trying to do? Is there a delegate method that only gets called when the users position is updated for the first time? Can I do this with just the MKMapView and forget CoreLocation altogether?
I've done it a few ways... in fact the framework has been changed a few times so I can't remember how I settled on it. The MKMapkit does have a method for showing user location, developer doc:
This property does not indicate whether the user’s position is actually visible on the map, only whether the map view should try to display it. Setting this property to YES causes the map view to use the Core Location framework to find the current location and try to display it on the map. As long as this property is YES, the map view continues to track the user’s location and update it periodically. The default value of this property is NO.
Showing the user’s location does not guarantee that the location is visible on the map. The user might have scrolled the map to a different point, causing the current location to be offscreen. To determine whether the user’s current location is currently displayed on the map, use the userLocationVisible property.
The user's location will appear as an annotation. You can get the annotation and zoom in on that. what would be better would be to use that flag in conjunction with the MKMapViewDelegate which has a method -mapView:DidUpdateUserslocation:
delegate documentation
Maybe after you get the first one you can change the map's tracking mode to none (i'm not sure if that removes the user location annotation or not, if it does, you can easily drop a map pin there with the proper skin). -setUserTrakingMode:animated: is the method on MKMap can use for that.
One thing I would caution on, in writing apps that work with user location in the past, it take a few seconds to get an accurate location. You might want to let the GPS ping a few times first before you lock in on a position. I've found that the first few ticks can be wildly inaccurate. Hope that helps.
I am working on an iOS 7 app that uses mapkit, and I can route and pot directions for users.
I am trying to recreate the exact same way the apple maps app works when starting a trip, with the voice coming up and narrating the steps, and the camera movements. I don;t know whether thats possible and where can I find classes that expose that.
Thanks.
About the narrating, I don't know. But for the traking of the user, and the camera movements, I used this library: https://github.com/100grams/CoreLocationUtils
There is a lot of helpers, I'm sure it could help you ;)
To rotate the view, you have to start the heading, like this:
// some code
[self.locationManager startUpdatingHeading];
// some code
Then, implement this delegate method:
-(void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading {
if (newHeading.headingAccuracy > 0 && abs(self.over-newHeading.trueHeading) > 3) {
CGFloat north = -1.0f * M_PI * (newHeading.trueHeading) / 180.0f;
CGFloat bearing = -1.0f * M_PI * (newHeading.trueHeading-self.bearing) / 180.0f;
/* do some verifications here
you can make the rotation here using CGAffineTransformMakeRotation(north)
or CGAffineTransformMakeRotation(bearing )
*/
self.over = newHeading.trueHeading;
}
}
Finaly, in this delegate method, you can get the bearing value between 2 coordinates using the library (previously mentionned):
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *newLocation = [locations lastObject];
self.currentUserLocation = newLocation;
// set the target location...
self.bearing = [CLLocation directionFromCoordinate:self.currentUserLocation.coordinate toCoordinate:self.targetLocation.coordinate];
}
Hope that will help you ;)
This features private to Apple Maps App.
You can just use maps or create this features by yourself.
Check out OpenEars for a narration option.
In iOS 7 and above, MKMapCamera and its animation ability will help you greatly with the actual stepping through on your map view.
First off, I am very new to iOS Dev and Objective-C. So please excuse any stupid questions or code.
I have been testing the location services on an iPhone. I have this code that is fired off by a NSTimer:
- (void)startLocationTracking
{
if(self.locationManager==nil){
_locationManager=[[CLLocationManager alloc] init];
_locationManager.delegate=self;
_locationManager.desiredAccuracy=kCLLocationAccuracyBest;
_locationManager.distanceFilter=1;
self.locationManager=_locationManager;
}
if([CLLocationManager locationServicesEnabled]){
[self.locationManager startUpdatingLocation];
}
}
And here is my location manager function:
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{
[self timerLog];
NSString *deviceID = [self getUUID];
double lat = newLocation.coordinate.latitude;
double lon = newLocation.coordinate.longitude;
double alt = newLocation.altitude;
double dir = newLocation.course;
double spd = newLocation.speed;
double ha = newLocation.horizontalAccuracy;
double va = newLocation.verticalAccuracy;
NSDateFormatter *formatter;
NSString *ts;
formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"yyyy-MM-dd HH:mm:ss.SSS"];
ts = [formatter stringFromDate:[NSDate date]];
[self geoTrackingWS :deviceID :lat :lon :alt :dir :spd :ha :va :ts];
[manager stopUpdatingLocation];
}
For some reason my function geoTrackingWS is firing off multiple times randomly. The NSTimer is running every 1 minute (just as a test) and sometimes it works fine and it calls the geoTrackingWS just once but other times it hits 2 or 3 times.
I have done logging and I can see my NSTimer is working fine and firing off as it should.
I have a feeling it has something to do with another application on my phone but I am not sure.
Any help or insight on this would be great.
Thanks
The location manager usually has multiple ways of determining your location, including:
cell tower triangulation;
wifi network identification;
GPS.
The first of those is least accurate but easiest to get, the second is usually more accurate but takes a while longer to figure out (as there's a network request for the lookup) and the final is most accurate but can take quite a while to figure out (searching for satellites, etc).
As a result it is expected behaviour that the location manager will respond with increasingly accurate results the longer it's left running. You can see the consequence of that when running Maps for example — it usually pins you down to quite a wide area quite quickly and then takes some time to get a more accurate estimate.
You probably don't want to create a new CLLocationManager instance more than once, which calling -startLocationTracking from a timer will do. From the look of your code, I think you want to call -startLocationTracking just once, and then the location API will send messages to your delegate (which you've specified as self) when it has new information. You might want to refresh your understanding of the delegate pattern, and look at some example code for using CLLocationManager. I hope that helps.
I've been playing around with the iPhone SDK, using MapKit and Core Location.
What are some of the tricks you can use to better test things... while still on the simulator (long before I have to try it out on my iPhone).
Is there a way to use NSTimer and regularly get 'pretend' values for location, heading, speed, etc?
The simulator only giving 1 location... and no movement... really limits its 'testing' usefulness.
It is normal way to receive the GPS data.
[GPS module] ----(CLLocationManagerDelegate)---> [YourLocationManager class]
locationManager:didUpdateToLocation:fromLocation:
This method will receive the data.
You can also call same method on YourLocationManager class from Test class.
[Test class] -------- call ------> [YourLocationManager class]
1.. make CLLocation object like this..... on Test class
CLLocationCoordinate2D location;
location.latitude = 37.0;
location.longitude = 127.0;
CLLocation *sampleLocation = [[CLLocation alloc] initWithCoordinate: location
altitude:100
horizontalAccuracy:100
verticalAccuracy:100
timestamp:[NSDate date]];
you can set only
latitude, longitude, altitude, hotizontal accuracy, vertical accuracy, timestamp.
you can't set... course, speed.
2.. call locationManager:didUpdateToLocation:fromLocation: method on YourLocationmanager class from Test class.
[yourLocationManager locationManager: nil or something
didUpdateToLocation: sampleLocation
fromLocation: sampleLocation or nil or something];
You can use NSTimer to send more data!!
You might wanna check out my FTLocationSimulator.
It reads a KML file generated by Google Earth to provide continuous location updates. It also updates the blue userLocation dot in a MKMapView with the simulated location updates.