I have a UIViewController where I want to display a map centered on current position.
AppDelegate's didFinishLaunchingWithOptions method looks like this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.locationManager = [[CLLocationManager alloc] init];
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined) {
[self.locationManager requestAlwaysAuthorization];
}
[self.locationManager startUpdatingLocation];
return YES;
}
My UIViewController.m class looks like this:
#interface GMapViewController ()<CLLocationManagerDelegate>
#property (weak, nonatomic) IBOutlet MKMapView *mapView;
#property (strong, nonatomic) CLLocation *currentLocation;
#end
#implementation GMapViewController
#pragma mark - Lifecycle methods
- (void)viewDidLoad {
[super viewDidLoad];
self.currentLocation = [CLLocation new];
[[[AppDelegate appDelegate] locationManager] setDelegate:self];
MKCoordinateRegion visibleRegion;
visibleRegion.center = self.currentLocation.coordinate;
visibleRegion.span = MKCoordinateSpanMake(200, 200);
[self.mapView setRegion:visibleRegion animated:YES];
}
#pragma mark - CLLocationManagerDelegate's methods
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
self.currentLocation = [locations lastObject];
}
The thing is the CLLocationManager's didUpdateLocations delegate method is called too late as the currentLocation property has no value when the viewDidLoad is called.
How can I get the current coordinate before viewDidLoad being called ?
You need to refresh your map view once the location has been updated; this will take some time.
You also have a potential race condition, because you call startUpdatingLocation in your AppDelegate, but the view controller isn't set as the delegate until viewDidLoad. The delegate will almost certainly be set before the location is updated, but you can't guarantee this.
I would suggest that you move everything into your view controller and if you only want to update the map once, use requestLocation:
#interface GMapViewController ()<CLLocationManagerDelegate>
#property (weak, nonatomic) IBOutlet MKMapView *mapView;
#property (strong, nonatomic) CLLocation *currentLocation;
#property (strong, nonatomic) CLLocationManager *locationManager;
#end
#implementation GMapViewController
#pragma mark - Lifecycle methods
- (void)viewDidLoad {
[super viewDidLoad];
self.locationManager = [[CLLocationManager alloc] init];
self.currentLocation = nil;
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined) {
[self.locationManager requestAlwaysAuthorization];
} else {
[self.locationManager requestLocation];
}
-(void) setMapTo:(CLLocation *)location {
MKCoordinateRegion visibleRegion;
visibleRegion.center =location.coordinate;
visibleRegion.span = MKCoordinateSpanMake(200, 200);
[self.mapView setRegion:visibleRegion animated:YES];
}
#pragma mark - CLLocationManagerDelegate's methods
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
CLLocation *currentLocation = [locations lastObject];
dispatch_async(dispatch_get_main_queue(), ^{
[self setMapTo:currentLocation];
});
}
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
if (status != kCLAuthorizationStatusRestricted && status != kCLAuthorizationStatusDenied) {
[manager requestLocation];
}
}
If you just want the map to track the user's location continually then you can simply set the map view's userTrackingMode property to MKUserTrackingModeFollow
Related
Im trying to get the current user location on the map view and have it be zoomed in on.
This my code-
#import "WhereamiAppDelegate.h"
#import "WhereamiViewController.h"
#implementation WhereamiAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.viewController = [[WhereamiViewController alloc] initWithNibName:#"WhereamiViewController" bundle:nil];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
#interface WhereamiViewController : UIViewController<CLLocationManagerDelegate,MKMapViewDelegate,UITextFieldDelegate>
{
//#public RootObject *rootObj;
CLLocationManager *locationManager;
IBOutlet MKMapView *worldView;
IBOutlet UIActivityIndicatorView *activityIndicator;
IBOutlet UITextField *locationTitleField;
}
-(IBAction)buttonDidGetPressed:(id)sender;
-(BOOL)textFieldShouldReturn:(UITextField *)textField;
-(void)findLocation;
-(void)foundLocation:(CLLocation *)loc;
#end
#implementation WhereamiViewController
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
NSLog(#"%#", NSStringFromSelector(_cmd));
if (self=[super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// rootObj= [[RootObject alloc] init];//RootObject initialised
// NSLog(#"RootObject–– %#",rootObj);
locationManager= [[CLLocationManager alloc] init];
[locationManager setDelegate:self];//self is Whereamicontroller. The delegate pointer is of type id<CLLocationManagerDelegate> and is an ivar of CLLocationManager.
[locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
}
return self;
}
-(void) viewDidLoad{
NSLog(#"%#",NSStringFromSelector(_cmd));
worldView.showsUserLocation=YES;
}
-(void)mapViewWillStartLocatingUser:(MKMapView *)mapView{
NSLog(#"%#", NSStringFromSelector(_cmd));
}
-(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation{
NSLog(#"%#",NSStringFromSelector(_cmd));
CLLocationCoordinate2D centerCoordinate= [userLocation coordinate]; //get the coordinate of current location.
NSLog(#"%# (%f, %f)",userLocation.location,centerCoordinate.latitude,centerCoordinate.longitude);
MKCoordinateSpan span= MKCoordinateSpanMake(250, 250);//Structure members
MKCoordinateRegion mapPortionToDisplay= MKCoordinateRegionMakeWithDistance(centerCoordinate, span.latitudeDelta, span.longitudeDelta);//span.latitudeDelta=250 and span.longitudeDelta=250
[worldView setRegion:mapPortionToDisplay animated:YES];
// [worldView setRegion:mapPortionToDisplay];
[worldView regionThatFits:mapPortionToDisplay];
}
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{ //CLLocationManagerDelegate method implementation
NSLog(#"%#", NSStringFromSelector(_cmd));
// NSTimeInterval t0=[[locations lastObject] timeIntervalSinceNow];
NSLog(#"current location–– %#",(CLLocation *)[locations lastObject]);
}
Following is the output-
2017-08-25 22:16:19.178 Whereami2[1601:758525] initWithNibName:bundle:
2017-08-25 22:16:19.294 Whereami2[1601:758525] viewDidLoad
2017-08-25 22:16:20.607 Whereami2[1601:758525] mapViewWillStartLocatingUser:
From the above lines, its clearly seen that mapView:didUpdateUserLocation: message did not get passed to map view's delegate at all. Same is the case with locationManager:didUpdateLocations:. The showsUserLocations property is supposed to send CLLocationManager's delegate, a viewcontroller (which is the same as MKMapView's delegate), this message also in its implementation. Is it not?
You've declared locationManager as an instance variable without synthesizing it's getters and setters. It's probably being deallocated. Just make it a property:
#interface WhereamiViewController : UIViewController <CLLocationManagerDelegate, MKMapViewDelegate, UITextFieldDelegate>
#property (nonatomic, strong) CLLocationManager *locationManager;
#end
As for the worldView, I don't see where you're setting something like worldView.delegate = self
I'm trying to create an app that ultimately will vibrate at certain locations. I just started learning Objective-C this week, and found this tutorial which seemed useful for initial testing: http://www.appcoda.com/how-to-get-current-location-iphone-user/
Instead of vibrating, I'm testing it so that the label changes at a designated coordinate. However, when I run the app it does not change the labels. In the bottom debug output bar in xCode it displays the coordinates, and they're the same as what I want them to be, but instead of changing the label "For Latitude Value" to "Success" it changes nothing. Here's the current code:
// ViewController.m
#import "ViewController.h"
#import CoreLocation;
#interface ViewController ()<CLLocationManagerDelegate>
#property (strong, nonatomic) CLLocationManager *locationManager;
#property (strong, nonatomic) CLGeocoder *geocoder;
#property (strong, nonatomic) CLPlacemark *placemark;
#end
#implementation ViewController {
CLLocationManager *locationManager;
}
- (void)requestAlwaysAuthorization{
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
locationManager = [[CLLocationManager alloc] init];
if ([locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]) {
[locationManager requestAlwaysAuthorization];
}
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
NSLog(#"%#", [locations lastObject]);
}
- (IBAction)getCurrentLocation:(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(#"didUpdateLocations: %#", newLocation);
CLLocation *currentLocation = newLocation;
if ((currentLocation.coordinate.longitude == 51.50998000)&&(currentLocation.coordinate.latitude == -0.13370000)) {
self.longitudeLabel.text = [NSString stringWithFormat:#"success"];
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
And here's the .h if that helps:
// ViewController.h
#import <UIKit/UIKit.h>
#import <AudioToolbox/AudioToolbox.h>
#import <CoreLocation/CoreLocation.h>
#interface ViewController : UIViewController <CLLocationManagerDelegate>
#property (weak, nonatomic) IBOutlet UILabel *latitudeLabel;
#property (weak, nonatomic) IBOutlet UILabel *longitudeLabel;
#property (weak, nonatomic) IBOutlet UILabel *addressLabel;
- (IBAction)getCurrentLocation:(id)sender;
#end
I've also tried it with:
if (currentLocation != nil) {
longitudeLabel.text = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.longitude];
latitudeLabel.text = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.latitude];
}
like they do in the tutorial where it should change the labels to display the GPS coordinates, but they don't change either. Again, it just shows the GPS coordinates in the debug bar.
Any ideas on why this isn't working? I apologize if this has already been covered somewhere, I've spent a few hours searching for and trying solutions online but since I'm new to this I may not know the right terms to search for. Any advice is greatly appreciated!
I would not recommend comparing two double values for equality. In theory they are never going to be equal. A better approach is to check whether the location is close enough to your reference location. Below an example from my code base. This is from a C file I use with utility functions. Here it handles float values, but you can do the same with double values.
int relativelyClose(CGFloat value, CGFloat reference, CGFloat precision)
{
return fabs(value - reference) / reference < range;
}
int relativelyClosePoints(CGPoint point, CGPoint r, CGFloat precision)
{
CGFloat dx = point.x -r.x;
CGFloat dy = point.y-r.y;
CGFloat distance = sqrt(dx*dx + dy*dy);
CGFloat reflength = sqrt(r.x*r.x + r.y*r.y);
return (distance < reflength * precision);
}
Additionally, to ensure that setting the label value is executed on the main thread, you can wrap those calls like this:
dispatch_async(dispatch_get_main_queue(), ^ {
longitudeLabel.text = ...;
latitudeLabel.text = ....;
});
I checked many links and googled it a lot. I tried those codes too but didn't succeeded so at last i am posting it here.
can any one help me regarding user location update.
I know how to show user current location it's just a check
showUserLocation.
I know there is location Manage and delegate for location keep updates.
I tried few code in which people are updating the map in location manager update delegate. But it's not working for me.It just show the blue point on user current location but it's not keep updating if i move.
So, can any one guide me from start that what should i do. How to show the User current location keep update on map when ever or where ever user move.
please try using this method
Don't forget to add CLLocationManagerDelegate and MKMapviewDelegate in your header File.
first implement the below line into your reload map method or viewDidload or viewWillappear method:
[mapView setShowsUserLocation:YES]
Than change the below methods like this:
(MKAnnotationView *) mapView:(MKMapView *)mapsView viewForAnnotation:(id <MKAnnotation>) annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
}
You can do it by yourself without using showUserLocation
You should use CLLocationManager to get current location and add it into mapView
Implement MKAnnotation
#interface MyAnnotation : NSObject<MKAnnotation>{
CLLocationCoordinate2D _coordinate;
NSString *_title;
NSString *_subtitle;
}
- (id)initWithCoordinate:(CLLocationCoordinate2D)coordinate title:(NSString *)title subtitle:(NSString *)subtitle;
#end
#implementation MyAnnotation
- (NSString *)title {return _title;}
- (NSString *)subtitle {return _subtitle;}
- (CLLocationCoordinate2D)coordinate {return _coordinate;}
- (void)setCoordinate:(CLLocationCoordinate2D)newCoordinate {_coordinate = newCoordinate;}
- (id)initWithCoordinate:(CLLocationCoordinate2D)coordinate title:(NSString *)title subtitle:(NSString *)subtitle {
if (self = [super init]) {
_title = title.copy;
_subtitle = subtitle.copy;
_coordinate = coordinate;
}
return self;
}
#end
Create 1 UIViewController
#interface MyViewController () <CLLocationManagerDelegate>{
CLLocationManager *locationManager;
MyAnnotation *userAnnotation;
}
#property (strong, nonatomic) MKMapView *mapView;
#end
#implementation MyViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.mapView = [[MKMapView alloc] initWithFrame:self.view.bounds];
self.mapView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
[self.view addSubview:self.mapView];
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
}
#pragma mark - DELEGATE
//for iOS 5
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
// [manager stopUpdatingLocation];
if (newLocation) {
if (!userAnnotation) {
userAnnotation = [[MyAnnotation alloc]
initWithCoordinate:newLocation.coordinate
title:#"I'm here"
subtitle:nil];
[self.mapView addAnnotation:userAnnotation];
} else {
[self.mapView removeAnnotation:userAnnotation];
[userAnnotation setCoordinate:newLocation.coordinate];
[self.mapView addAnnotation:userAnnotation];
}
}
}
//for iOS 6
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
CLLocation *loc = locations.lastObject;
[self locationManager:manager didUpdateToLocation:loc fromLocation:nil];
}
- (void)locationManager:(CLLocationManager *)manager
didFailWithError:(NSError *)error {
//open GPS services please
}
- (void)dealloc {
[locationManager stopUpdatingLocation];
}
#end
Note:
Remember remove old annotation and add it again when receiving new
location
Please use iOS 5 and ARC
I have a simply app written in Objective-C that gets my current position using CLLocationManager, etc. The code works fine on the iPhone 6.1 Simulator, but it doesn't work on my actual iPod touch. The iPod Touch 3rd Gen is running iOS5.1, and it does ask for the user to enable location services, however it does not get updated. I can post code if you ask, but I thought that there would just be a glaring compatibility issue that would be a rather simple fix.
Any help would be appreciated!
Thanks!
~Carpetfizz
ViewController.h
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#interface ViewController : UIViewController <CLLocationManagerDelegate>
{
CLLocationManager* locationManager;
}
#property (weak, nonatomic) IBOutlet UILabel *labelLong;
#property (weak, nonatomic) IBOutlet UILabel *labelLat;
-(IBAction)updateLocaitonButtonAction:(id)sender;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize labelLong,labelLat;
- (void)viewDidLoad
{
[super viewDidLoad];
locationManager = [[CLLocationManager alloc]init];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
}
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *location = [locations lastObject];
NSLog(#"%f,%f",location.coordinate.latitude,location.coordinate.longitude);
self.labelLong.text = [NSString stringWithFormat:#"%f",location.coordinate.longitude];
self.labelLat.text = [NSString stringWithFormat:#"%f",location.coordinate.latitude];
[locationManager stopUpdatingLocation];
}
-(IBAction)updateLocaitonButtonAction:(id)sender
{
[locationManager startUpdatingLocation];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Simply use this delegate
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
CLLocation *location = newLocation;
NSLog(#"%f,%f",location.coordinate.latitude,location.coordinate.longitude);
}
I am working on an iOS application and want to include the Breadcrumb iOS Mapkit route functionality provided by apple as one of the feature. I have created a UIViewController in the storyboard (as a tab from a tab bar controller) and inserted a MKMapView in it. I have also connected it to the outlet in ThirdViewController shown below. The classes are shown below. I have classes CrumbPath and CrumbPathView exactly as in the Breadcrumb example at http://developer.apple.com/library/ios/#samplecode/Breadcrumb/Introduction/Intro.html
Even with the same code, the mkoverlay route does not show in my app. Am I missing something important here. I am not experienced in iOS programming and may have missed something basic.
ThirdViewController.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#import "CrumbPath.h"
#import "CrumbPathView.h"
#interface ThirdViewController : UIViewController <MKMapViewDelegate, CLLocationManagerDelegate>
{
#private
MKMapView *map;
CrumbPath *crumbs;
CrumbPathView *crumbView;
CLLocationManager *locationManager;
}
#property (nonatomic, retain) IBOutlet MKMapView *map;
#property (nonatomic, retain) CLLocationManager *locationManager;
#end
ThirdViewController.m
#import "ThirdViewController.h"
#interface ThirdViewController ()
#end
#implementation ThirdViewController
#synthesize locationManager, map;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.wantsFullScreenLayout = YES;
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[self.locationManager startUpdatingLocation];
[self.view addSubview:self.map];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
self.map = nil;
self.locationManager.delegate = nil;
self.locationManager = nil;
}
-(void) dealloc
{
}
- (BOOL)shouldAutorotateToInterfaceOrientation (UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark -
#pragma mark MapKit
- (void) locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
if(newLocation)
{
if((oldLocation.coordinate.latitude != newLocation.coordinate.latitude) && (oldLocation.coordinate.longitude != newLocation.coordinate.longitude))
{
if(!crumbs)
{
crumbs = [[CrumbPath alloc] initWithCenterCoordinate:newLocation.coordinate];
[map addOverlay:crumbs];
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(newLocation.coordinate, 2000, 2000);
[map setRegion:region animated:YES];
}
else
{
MKMapRect updateRect = [crumbs addCoordinate:newLocation.coordinate];
if(!MKMapRectIsNull(updateRect))
{
MKZoomScale currentZoomScale = (CGFloat)(map.bounds.size.width/map.visibleMapRect.size.width);
CGFloat lineWidth = MKRoadWidthAtZoomScale(currentZoomScale);
updateRect = MKMapRectInset(updateRect, -lineWidth, -lineWidth);
[crumbView setNeedsDisplayInMapRect:updateRect];
}
}
}
}
}
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay
{
if(!crumbView)
{
crumbView = [[CrumbPathView alloc] initWithOverlay:overlay];
}
return crumbView;
}
#end
You didn't have your ThirdViewController set as the delegate to your MKMapView in your storyboard, so mapView:viewForOverlay: was never being called. Setting the delegate property fixes the problem.