I init my mapviewControler like this:
- (void)viewDidLoad
{
[super viewDidLoad];
self.firstShow=TRUE;
self.mapView.delegate=self;
self.mapView.showsUserLocation=YES;
self.cllmng=[[CLLocationManager alloc]init];
self.cllmng.delegate=self;
self.cllmng.desiredAccuracy=kCLLocationAccuracyNearestTenMeters;
self.cllmng.distanceFilter=50;
[self.cllmng startUpdatingLocation];
}
and I make mapviewControler implement CLLocationManagerDelegate, and implement - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations in my code.
The problem I have is that I want to init map view centred on current user location with a proper scale. I intend to do this by
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
CLLocation * currentLoci=[locations lastObject];
MKCoordinateRegion r;
MKCoordinateSpan span;
span.latitudeDelta = 1;
span.longitudeDelta = 1;
r.span = span;
CLLocationCoordinate2D c;
c.longitude=currentLoci.coordinate.longitude;
c.latitude=currentLoci.coordinate.latitude;
r.center = c;
[self.mapView setRegion:r animated:YES];
}
But sometimes, after [self.cllmng startUpdatingLocation] called in the -(void) viewDidload, I cannot get a call of - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations immediately. Thus, the map view just init showing user location but showing the whole Australia. How can I set the scale/span in the init of map view even didUpdateLocations is not triggered? THX!
INstead of modifying the region yourself using a CLLocation manager you could just set the trackingMode of the mapView and it'll center and zoom in on your position automatically. It will be possible for the user to disable the tracking mode if they start dragging the map around, but you can disable user interaction if you really want them to have no control. Here is how to do it
[self.mapView setUserTrackingMode:MKUserTrackingModeFollow animated:YES];
ref: https://developer.apple.com/library/ios/documentation/MapKit/Reference/MKMapView_Class/MKMapView/MKMapView.html#//apple_ref/occ/instm/MKMapView/setUserTrackingMode:animated:
iOS 11.x Swift 4.0 Craigs answer, updated.
mapView.showsUserLocation = true
Not so obvious I fear.
Related
I have worked on one application name time tracker. User can manually swipe in and swipe out manually by clicking the button.
Now I would like to make it as automatic based on the location detection. For that I am using CLLocationManager class. It works fine sometimes and sometimes it gives wrong swipe details. I am using below code.
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
[locationManager requestWhenInUseAuthorization];
[locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
_latitude.text = [NSString stringWithFormat:#"Latitude: %f", newLocation.coordinate.latitude];
_longitude.text = [NSString stringWithFormat:#"Longitude: %f", newLocation.coordinate.longitude];
if([_latitude.text doubleValue] > 17.76890) && [_longitude.text doubleValue] > 78.34567) {
if (isSwipeIn) {
isSwipeIn = false;
//necessary swipe out UI and logic
} else {
isSwipeIn = true;
//necessary swipe in UI and logic
}
}
}
Can anyone help me on this..
Instead of comparing lat-long, go for a range check like if your device is within few meter mark as swipe in otherwise swipe out.
You can check distance between two lat-long using following method in Objective-C
CLLocation *location; // Your Location to compare
CLLocation *currentLocation; // Your current location
double distance = [location distanceFromLocation:currentLocation]; // Returns distance in meters
// Now lets say you are within 5 meters mark Swipe In
if(distance <= 5)
// Mark swipe IN
else
// Mark swipe OUT
I hope this will help you. Happy coding :)
There is another way to do this, you can get distance from target location and check it with horizontalAccuracy of current location.
The delta gives you the distance between current location and targeted location. If delta is less than (<) horizontalAccuracy than current location is in a circle with a radius of horizontalAccuracy.
If delta is greater than (>) horizontalAccuracy than current location is far away than your targeted location.
So now CLLocationManager delegate method will be looks like below:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
_latitude.text = [NSString stringWithFormat:#"Latitude: %f", newLocation.coordinate.latitude];
_longitude.text = [NSString stringWithFormat:#"Longitude: %f", newLocation.coordinate.longitude];
// Create Location object for your target location. e.g. (17.76890,78.34567)
CLLocation *targetLocation = [[CLLocation alloc] initWithLatitude:17.76890 longitude:78.34567];
CLLocationDistance delta = [newLocation distanceFromLocation:targetLocation];
if (delta > newLocation.horizontalAccuracy) {
if (isSwipeIn) {
isSwipeIn = false;
//necessary swipe out UI and logic
} else {
isSwipeIn = true;
//necessary swipe in UI and logic
}
}
}
In my app, I am using GMSMapView, and I would like to change tracking mode. In iOS MapKit, I can change the tracking mode to MKUserTrackingModeFollowWithHeading, but don't know how to change it in GMSMapView.
In the app Google Maps, it is working after second touch on myLocationButton. Is it possible?
For continuously changing the camera with the current location, you will need to update the GMSCamera for google maps to current location. You can do it in Location Manager delegate method.
CLLocation *location;
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
//Get current latitude and longitude from didUpdateLocation
location = [locations lastObject];
}
-(void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:location.coordinate.latitude longitude:location.coordinate.longitude zoom:10 bearing:newHeading.trueHeading viewingAngle:0];
//You can change viewingAngle from 0 to 45
[self.mapForView animateToCameraPosition:camera];
}
In case your delegate is not getting called, take help from my answer here
Hope it helps.
I've got an issue. I'm using a MKMapView for displaying some annotations. I initialize a map view with default zoom. And it displays some map.
but when i try zooming, tiles not loaded, and map becomes empty. Like this.
I create my Map View via interface builder.
#property (nonatomic, weak) IBOutlet MKMapView* mapView;
What am I doing wrong? Is there any mandatory methods for implementation, that affect on this functional? And yes, there is an internet connection on my device.
Generally this can happens due to internet connection. If you have slow internet connection than it takes time to load map tiles.
About methods i recommended to override below method.
Override MKMapView delegate method -
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
Will call every time you zoom in/out and load map tiles.
PS - provide MKMapViewDelegate to your view controller.
I had the same loading problem till I got the problem working by googling!
Note: Effective ios 8 & later, we need to add a value NSLocationWhenInUseUsageDescription in Info.plist file with our own value as description. This is because showsUserLocation property of **MKMapView** doesn't work straight away. More info here !!!!
//This should be declared in .h file
#property(nonatomic, strong) CLLocationManager *locationManager;
- (void)viewDidLoad {
self.mapView.showsUserLocation = YES;
self.mapView.delegate = self;
self.locationManager.delegate = self;
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.distanceFilter = kCLDistanceFilterNone;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
#ifdef __IPHONE_8_0
if(IS_OS_8_OR_LATER) {
[self.locationManager requestWhenInUseAuthorization];
}
#endif
[self.locationManager startUpdatingLocation];
self.locationManager.distanceFilter = kCLDistanceFilterNone;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
}
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
if (status == kCLAuthorizationStatusAuthorizedAlways || status == kCLAuthorizationStatusAuthorizedWhenInUse) {
self.mapView.showsUserLocation = YES;
}
}
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
MKCoordinateRegion region;
region.center = self.locationView.userLocation.coordinate;
MKCoordinateSpan span;
span.latitudeDelta = 0.015; ->Adjust this value to zoom as per your requirement
span.longitudeDelta = 0.015;
region.span = span;
[self.mapView setRegion:region animated:YES];
}
I firmly believe this will yield the expected result without fail, i.e. MKMapView zoom to the current user location.
My question is as follows:
When is the location updated when using Location Services? When I called startUpdatingLocation I expected to already have a location returned so I can retrieve latitude and longitude for my iOS project. These are required parameters for a web service as well but it seems they are returned as nil.
The interface conforms to CLLocationManagerDelegate protocol and I have implemented the methods for it. Anyway here is my code:
- (void)viewDidLoad
{
super viewDidLoad];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
if([self.parentViewController isKindOfClass:[BTMainViewController class]])
{
BTMainViewController *parent = (BTMainViewController *)self.parentViewController;
self.sessionKey = parent.session;
NSLog(#"URL is %# ", self.sessionKey);
}
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
// also set the URL
self.serviceURL = [apiURL stringByAppendingString:#"/get_employee_closestlocations"];
// set tableview delegate and data source
self.tableView.delegate = self;
self.tableView.dataSource = self;
// adjust for EdgeInset with navigation bar.
self.tableView.contentInset = UIEdgeInsetsMake(64.0f, 0.0f, 0.0f, 0.0f);
// fetch the locations here
[locationManager startUpdatingLocation];
[self fetchLocations];
}
didUpdateToLocation implementation
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
CLLocation *currentLocation = [locationManager location];
[locationManager stopUpdatingLocation];
if(currentLocation != nil)
{
[self setLongitude:[NSNumber numberWithDouble: currentLocation.coordinate.longitude]];
[self setLatitude:[NSNumber numberWithDouble: currentLocation.coordinate.latitude]];
}
}
Any suggestions would be welcome and thanks in advance!
The delegate method you are using is deprecated. You should use locationManager:didUpdateLocations: and then access the location update from the end of the array -
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *currentLocation = (CLLocation *)[locations lastObject];
...
}
It can take some time to get a location fix, particularly as you have specified kCLLocationAccuracyBest - iOS may need to start up the GPS receiver if it hasn't been used recently and then the GPS needs to obtain a fix - if the device is inside or has bad GPS reception this can further delay the acquisition of a location. You can get an idea of the time to obtain a fix by restarting your device, starting the maps application and tapping the location "arrow" and waiting until the blue location circle collapses down to the blue & white marker.
I would suggest that you invoke your [self fetchLocations]; from the didUpdateLocations method
Also, the Core Location documentation states -
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.
So, there is a risk that when you do access the location, it may not be particularly accurate. You can look at the horizontalAccuracy property of the CLLocation and decide whether you want to accept this location or wait for a more accurate location (bearing in mind that it may not arrive if the device is inside or has poor reception)
You need to do in viewDidLoad like this
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
mapView.delegate = self;
mapView.showsUserLocation = YES; // Enable it when we want to track user's current location.
}
after doing this the below delegate method will automatically called.
- (void)mapView:(MKMapView *)mapView
didUpdateUserLocation:
(MKUserLocation *)userLocation
{
self.mapView.centerCoordinate = userLocation.location.coordinate;
}
I'm trying to implement a function of a Geo location for a user, I'm statically setting up latitude and longitude information, when app starts if the user is within that area I'm showing up a message that "You've been reached to office" else "You're going out from office". I've implemented below code to achieve this, I tried by moving all around by steps and on vehicles, but in both the cases it always shows that "You've been reached to office", however I was 2km away from that location! I think the problem is in the comparison of Geo data in CLLocationManager delegate.
- (void) startUpdateUserLocation
{
if(!locationManager)
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
// [locationManager startUpdatingLocation];
CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(latitude, longitude);
CLRegion *region = [[CLRegion alloc] initCircularRegionWithCenter:coord radius:kCLDistanceFilterNone identifier:#"identifier"];
[locationManager startMonitoringForRegion:region];
}
- (void)viewDidLoad
{
[super viewDidLoad];
latitude = 23.076289;
longitude = 72.508129;
}
- (void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
MKCoordinateRegion region;
region.center = mapView.userLocation.coordinate;
region.span = MKCoordinateSpanMake(0.25, 0.25);
region = [mapView regionThatFits:region];
[mapView setRegion:region animated:YES];
lblCurrentCoords.text = [NSString stringWithFormat:#"lat %f lon %f",mapView.userLocation.coordinate.latitude,mapView.userLocation.coordinate.longitude];
[self startUpdateUserLocation];
}
- (void)locationManager:(CLLocationManager *)manager
didEnterRegion:(CLRegion *)region __OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_0)
{
[listOfPoints addObject:manager.location];
[tablePoints reloadData];
/*
* locationManager:didEnterRegion:
*
* Discussion:
* Invoked when the user enters a monitored region. This callback will be invoked for every allocated
* CLLocationManager instance with a non-nil delegate that implements this method.
*/
lblLocationStatus.text = #"You're in office area!...";
}
- (void)locationManager:(CLLocationManager *)manager
didExitRegion:(CLRegion *)region __OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_0)
{
/*
* locationManager:didExitRegion:
*
* Discussion:
* Invoked when the user exits a monitored region. This callback will be invoked for every allocated
* CLLocationManager instance with a non-nil delegate that implements this method.
*/
lblLocationStatus.text = #"You're going out from office area!...";
}
- (void)locationManager:(CLLocationManager *)manager
didStartMonitoringForRegion:(CLRegion *)region __OSX_AVAILABLE_STARTING(__MAC_TBD,__IPHONE_5_0)
{
/*
* locationManager:didStartMonitoringForRegion:
*
* Discussion:
* Invoked when a monitoring for a region started successfully.
*/
lblLocationStatus.text = #"Start monitoring...";
}
- (void)locationManager:(CLLocationManager *)manager
monitoringDidFailForRegion:(CLRegion *)region
withError:(NSError *)error __OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_0)
{
/*
* locationManager:monitoringDidFailForRegion:withError:
*
* Discussion:
* Invoked when a region monitoring error has occurred. Error types are defined in "CLError.h".
*/
lblLocationStatus.text = #"Stop monitoring...";
}
I am trying to accomplish the following things!
If the user entered into the Geo location, he should be "alert". --- How to match location?
If moving around within that Geo location then the code should be monitoring this activity! --- Need to set a desired accuracy property?
I want my code to check constantly for the user Geo location, how do I do that? --- Need to call function in NSTimer ?
I found many questions on SO asked about the same but NO one has matched answers! Someone please guide me whether I'm going in the right direction or not as this code doesn't show up! :)
It sounds like you should be using region monitoring instead, which tells you when the user enters or exits a circular area. Set it up with startMonitoringForRegion: and implement the CLLocationManagerDelegate methods
– locationManager:didEnterRegion:
– locationManager:didExitRegion:
– locationManager:monitoringDidFailForRegion:withError:
– locationManager:didStartMonitoringForRegion:
If you're having trouble with bad location data coming in, check for the age of the CLLocation in locationManager:didUpdateLocations: or locationManager:didUpdateToLocation:fromLocation:. If it's more than 60 seconds old, don't use it.