Why is this code updating location twice every time? - objective-c

Here is the code, it all works fine but each time I hit the "Get My Location" button it updates the location twice, I cannot find the reason why, any idea's? I have removed much code from below this and it still does it so I know it's in this part somewhere. Thanks.
.h file:
#import <CoreLocation/CoreLocation.h>
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *LatitudeLabel;
#property (weak, nonatomic) IBOutlet UILabel *LongitudeLabel;
#property (weak, nonatomic) IBOutlet UILabel *GPSAccuracyLabel;
#property (weak, nonatomic) IBOutlet UILabel *AltitudeLabel;
#property (weak, nonatomic) IBOutlet UILabel *VerticalAccuracyLabel;
- (IBAction)getCurrentLocation:(id)sender;
#end
#interface MyLocationViewController : UIViewController <CLLocationManagerDelegate>
#end
.m file:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController {
CLLocationManager *locationManager;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
locationManager = [[CLLocationManager alloc] init];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)getCurrentLocation:(id)sender {
locationManager.delegate = (id)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(#"Location updated: %#", newLocation);
CLLocation *currentLocation = newLocation;
if (currentLocation != nil) {
_LatitudeLabel.text = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.latitude];
_LongitudeLabel.text = [NSString stringWithFormat:#"%.6f", currentLocation.coordinate.longitude];
_GPSAccuracyLabel.text = [NSString stringWithFormat:#"%.2f", currentLocation.horizontalAccuracy];
_AltitudeLabel.text = [NSString stringWithFormat:#"%.2f", currentLocation.altitude];
_VerticalAccuracyLabel.text = [NSString stringWithFormat:#"%.2f", currentLocation.verticalAccuracy];
}
Console output each time I press the button:
2014-11-22 23:49:37.539 MyLocationDemo[914:60b] Location updated: <+10.16863927,+124.75859298> +/- 10.00m (speed 0.00 mps / course -1.00) # 22/11/14 11:49:37 pm Philippine Standard Time
2014-11-22 23:49:37.545 MyLocationDemo[914:60b] Location updated: <+10.16863927,+124.75859298> +/- 10.00m (speed 0.00 mps / course -1.00) # 22/11/14 11:49:37 pm Philippine Standard Time

After a quick look at Apple's documentations, I've noticed that the delegate method you are using, - locationManager:didUpdateToLocation:fromLocation: is deprecated since iOS 6.
Instead, you should use - locationManager:didUpdateLocations:.
Try replacing your code with the code below and see if it make any difference:
EDIT- I've edit the code below to handle the double-call you get.
From your post above I see they are almost simultaneously, So basically we'll check if at least 1 seconds has passed since last call.
There are probably better ways to do it, but that was just at the top of my head...
Haven't checked it in Xcode, but, if I haven't made a typo or something, it should work.
// ViewController.m
#interface ViewController ()
#property (nonatomic, strong) NSDate *lastUpdateTime; // Create a property
#end // to hold current time
- (void)viewDidLoad {
[super viewDidLoad];
self.lastUpdateTime = [NSDate date]; // In viewDidLoad, 'initialize' it
// to get the current time
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations
{
NSTimeInterval passedTime = -[self.lastUpdateTime timeIntervalSinceNow];
// Here we are checking how much seconds have passed since our lastUpdateTime
// Since lastUpdateTime is in the past, the result will be negative, therefore
// the minus sign, so we'll get a positive number
if(passedTime < 1) {
return;
} // Now we check if less than one second have passed. If so, the whole method
// will return. If not, it will just continue executing
CLLocation *currentLocation = [locations lastObject];
self.lastUpdateTime = [NSDate date]; // Don't forget to update the lastUpdateTime
// To hold the new update time
if (currentLocation != nil) {
NSLog(#"Location updated: %#", currentLocation);
_LatitudeLabel.text = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.latitude];
_LongitudeLabel.text = [NSString stringWithFormat:#"%.6f", currentLocation.coordinate.longitude];
_GPSAccuracyLabel.text = [NSString stringWithFormat:#"%.2f", currentLocation.horizontalAccuracy];
_AltitudeLabel.text = [NSString stringWithFormat:#"%.2f", currentLocation.altitude];
_VerticalAccuracyLabel.text = [NSString stringWithFormat:#"%.2f", currentLocation.verticalAccuracy];
}
}

#AMI289, your idea worked, no more double call.
I post the final code here in case it helps others, I just added back the locationManager = [[CLLocationManager alloc] init];.
// ViewController.m
#import "ViewController.h"
#interface ViewController ()
#property (nonatomic, strong) NSDate *lastUpdateTime; // create a property to hold current time.
#end
#implementation ViewController {
CLLocationManager *locationManager;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.lastUpdateTime = [NSDate date]; // In viewDidLoad, 'initialize' it to get the current time
locationManager = [[CLLocationManager alloc] init];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)getCurrentLocation:(id)sender {
locationManager.delegate = (id)self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
}
#pragma mark - CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations
{
NSTimeInterval passedTime = -[self.lastUpdateTime timeIntervalSinceNow];
// Here we are checking how much seconds have passed since our lastUpdateTime
// Since lastUpdateTime is in the past, the result will be negative, therefore
// the minus sign, so we'll get a positive number
if(passedTime < 1) {
return;
} // Now we check if less than one second have passed. If so, the whole method
// will return. If not, it will just continue executing
CLLocation *currentLocation = [locations lastObject];
self.lastUpdateTime = [NSDate date]; // Don't forget to update the lastUpdateTime
// To hold the new update time
if (currentLocation != nil) {
NSLog(#"Location updated: %#", currentLocation);
_LatutideLabel.text = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.latitude];
_LongitudeLabel.text = [NSString stringWithFormat:#"%.6f", currentLocation.coordinate.longitude];
_GPSAccuracyLabel.text = [NSString stringWithFormat:#"%.2f", currentLocation.horizontalAccuracy];
_AltitudeLabel.text = [NSString stringWithFormat:#"%.2f", currentLocation.altitude];
_VerticalAccuracyLabel.text = [NSString stringWithFormat:#"%.2f", currentLocation.verticalAccuracy];
}
// Stop Location Manager
[locationManager stopUpdatingLocation];
}
#end

Related

how to show longitude, latitude in label of this same code

//ViewController.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#import <CoreLocation/CoreLocation.h>
#interface ViewController : UIViewController<CLLocationManagerDelegate,MKMapViewDelegate>
#property (weak, nonatomic) IBOutlet UILabel *addresslbl;
#property (weak, nonatomic) IBOutlet MKMapView *mapView;
#end
//Viewcontroller.m
#import "ViewController.h"
#interface ViewController ()
{
CLLocationManager *locationManager;
CLLocationCoordinate2D updateLocation;
NSString *strAddress;
CLGeocoder *geocoder;
}
#end
#implementation ViewController
#define AzaveaCoordinate CLLocationCoordinate2DMake(19.167391, 73.247686)
- (void)viewDidLoad {
[super viewDidLoad];
[_mapView setRegion:MKCoordinateRegionMake(AzaveaCoordinate, MKCoordinateSpanMake(0.010, 0.010)) animated:YES];
geocoder = [[CLGeocoder alloc]init];
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager requestWhenInUseAuthorization];
self.mapView.delegate = self;
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(#"didFailWithError: %#", error);
}
-(void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
CLLocation *location = [[CLLocation alloc]initWithLatitude:mapView.centerCoordinate.latitude longitude:mapView.centerCoordinate.longitude];
[self geocode:location];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation
{
CLLocation *currentLocation = newLocation;
self.mapView.centerCoordinate = currentLocation.coordinate;
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(currentLocation.coordinate,1500,1500);
[self.mapView setRegion: region animated:true];
[self geocode:currentLocation];
}
-(void)geocode:(CLLocation *)location
{
// geocoder = [[CLGeocoder alloc]init];
[geocoder cancelGeocode];
[geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *data, NSError *error)
{
CLPlacemark *placemark = [data lastObject];
NSDictionary * addressDict = placemark.addressDictionary;
NSArray * addressList = addressDict[#"FormattedAddressLines"];
NSString * address = [NSString stringWithFormat:#"%#,%#,%#",addressList[0],addressList[1],addressList[2]];
NSLog(#" Address is :%#",address);
self.addresslbl.text = address;
}];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
This is the moveable map code like ola and uber. Means when i move the map, at that time the map pin is noted, the noted pin is given me address of the that particular area like city, state, pincode etc in label, but i want only the longitude, latitude value of this address in label.
so please help me about this code. How can i get the longitude latitude value in ?
Assuming that the label that you want to is self.addresslbl, change this method:
-(void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
CLLocation *location = [[CLLocation alloc]initWithLatitude:mapView.centerCoordinate.latitude longitude:mapView.centerCoordinate.longitude];
[self geocode:location];
}
To:
-(void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
self.addresslbl.text = [NSString stringWithFormat:#"%f, %f", mapView.centerCoordinate.longitude, mapView.centerCoordinate.latitude];
}

Use of undeclared identifier 'locationManager'

I'm trying to follow this tutorial:
http://www.appcoda.com/how-to-get-current-location-iphone-user/
Everything is fine till I add this line:
locationManager = [[CLLocationManager alloc] init];
Then I get the error.
I also get errors for these lines: (Xcode suggests I use "_LongitudeLabel"?
if (currentLocation != nil) {
longitudeLabel.text = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.longitude];
latitudeLabel.text = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.latitude];
}
Any idea what's wrong? Does the tutorial have errors or have I done something wrong?
Thanks!
This is ViewController.m file:
#import "ViewController.h"
#implementation MyLocationViewController {
CLLocationManager *locationManager;
}
#end
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
locationManager = [[CLLocationManager alloc] init];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (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(#"didUpdateToLocation: %#", newLocation);
CLLocation *currentLocation = newLocation;
if (currentLocation != nil) {
longitudeLabel.text = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.longitude];
latitudeLabel.text = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.latitude];
}
}
#end
This is ViewController.h file:
// ViewController.h
// MyLocationDemo
//
// Created by Ian Nicoll on 12/11/14.
// Copyright (c) 2014 Ian Nicoll. All rights reserved.
//
#import <CoreLocation/CoreLocation.h>
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *LatitudeLabel;
#property (weak, nonatomic) IBOutlet UILabel *LongitudeLabel;
#property (weak, nonatomic) IBOutlet UILabel *addressLabel;
- (IBAction)getCurrentLocation:(id)sender;
#end
#interface MyLocationViewController : UIViewController <CLLocationManagerDelegate>
#end
The first issue is an error on your part. You declared locationManager in MyLocationViewController but try to initialize it in the viewDidLoad of ViewController, where it of course does not exist.
The second issue is an issue with the instructions. When you declare an #property, the default behavior is to create an instance variable with an underscore in front of it.
So #property (weak, nonatomic) IBOutlet UILabel *LatitudeLabel; can be accessed as self.latitudeLabel (which goes through the setter/getter) or just _latitudeLabel which accesses the ivar directly. The latter is probably what you want.
Ok so the build now is Succeeded (though I'm not 100& sure I got things right) but now I get a warning for this line: locationManager.delegate = self; - Assigning to 'id'from incompatible type 'ViewControler *const_strong'
Would you know how to fix this warning?
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController {
CLLocationManager *locationManager;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
locationManager = [[CLLocationManager alloc] init];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (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(#"didUpdateToLocation: %#", newLocation);
CLLocation *currentLocation = newLocation;
if (currentLocation != nil) {
_LongitudeLabel.text = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.longitude];
_LatitudeLabel.text = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.latitude];
}
}
#end

Problems with Location Manager

I've been stuck on this problem for ages and I'm sure the reason is that I don't know enough about objective-c. I've only just started using coding with it!
Anyway I'm trying to design an app that tracks the current distance travelled and also displays the route that have been taken using MKPolyLine. I've got MKPolyLine sorted now but the problem I;m having is with getting the current distance. I think the code is OK but I think i'm doing something funny with location manager.
I've done a bit of debugging and found that my second locationManager method does not get called.
Here is the code below, if someone can take a quick look and just point me in the right direct it would be greatly appreciated.
Many Thanks.
.m :
#interface StartCycleViewController ()
#property (nonatomic, strong) CLLocationManager *locationManager;
#property (strong, nonatomic) IBOutlet MKMapView *mapView;
#property (nonatomic, strong) UIView *containerView;
#property (nonatomic, strong) CLLocationManager *distanceManager;
#end
#implementation StartCycleViewController
static double totalDistanceBetween;
#synthesize cycleLocation = _cycleLocation;
#synthesize currentCycleLocation = _currentCycleLocation;
#synthesize mapView;
- (void)viewDidLoad
{
[super viewDidLoad];
[self startCycleLocation];
[mapView setDelegate:self];
self.locations = [NSMutableArray array];
}
- (void)dealloc
{
self.locationManager.delegate = nil;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - startCycleLocation
- (void)startCycleLocation{
if (!_cycleLocation){
_cycleLocation = [[CLLocationManager alloc]init];
_cycleLocation.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
_cycleLocation.distanceFilter = 5;
_cycleLocation.delegate = self;
_startLocation = nil;
}
[_cycleLocation startUpdatingLocation];
}
- (void) locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
NSLog(#"%#",error);
if ( [error code] != kCLErrorLocationUnknown ){
[self stopLocationManager];
}
}
#pragma mark - CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *location = [locations lastObject];
if (location.horizontalAccuracy < 0)
return;
[self.locations addObject:location];
NSUInteger count = [self.locations count];
if (count > 1) {
// only both to add new location if distance has changed by more than 5 meters
[self.locations addObject:location];
count = [self.locations count];
CLLocationCoordinate2D coordinates[count];
for (NSInteger i = 0; i < count; i++) {
coordinates[i] = [(CLLocation *)self.locations[i] coordinate];
}
MKPolyline *oldPolyline = self.polyline;
self.polyline = [MKPolyline polylineWithCoordinates:coordinates count:count];
[self.mapView addOverlay:self.polyline];
if (oldPolyline)
[self.mapView removeOverlay:oldPolyline];
}
NSLog(#"didUpdateToLocation2: %#", location);
}
- (void) stopLocationManager {
[self.cycleLocation stopUpdatingLocation];
}
#pragma mark - MKMapViewDelegate
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
{
NSLog(#"PolyLine Started");
if ([overlay isKindOfClass:[MKPolyline class]])
{
MKPolylineRenderer *renderer = [[MKPolylineRenderer alloc] initWithPolyline:overlay];
[self.mapView setVisibleMapRect:overlay.boundingMapRect animated:YES];
renderer.strokeColor = [[UIColor redColor] colorWithAlphaComponent:0.7];
renderer.lineWidth = 3;
return renderer;
NSLog(#"MKPolyline Rendering");
}
return nil;
}
- (IBAction)stopActivity:(id)sender {
[self stopLocationManager];
NSLog(#"Stopped");
}
-(void)distanceManager:(CLLocationManager *)manager didUpdateLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
NSLog(#"Current Distance Started");
if (self.startLocation == nil)
{
totalDistanceBetween = 0;
_startLocation = newLocation;
}
CLLocationDistance distanceBetween = [newLocation distanceFromLocation:_startLocation ];
self.startLocation = newLocation;
totalDistanceBetween += distanceBetween;
NSString *cycleDistanceString = [[NSString alloc]
initWithFormat:#"%f",
totalDistanceBetween];
_CurrentDistance.text = cycleDistanceString;
NSLog(#"cycleDistance is %#", cycleDistanceString);
}
#end
.h :
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import "ViewController.h"
#import <MapKit/MapKit.h>
#import <AVFoundation/AVFoundation.h>
#interface StartCycleViewController : UIViewController <NSXMLParserDelegate, CLLocationManagerDelegate, MKMapViewDelegate>
#property (weak, nonatomic) IBOutlet UILabel *longitudeLabel;
#property (weak, nonatomic) IBOutlet UILabel *latitudeLabel;
#property (nonatomic,strong) CLLocation *currentCycleLocation;
#property (nonatomic,strong) CLLocationManager *cycleLocation;
#property (nonatomic,strong) CLLocation *startLocation;
#property (strong, nonatomic) IBOutlet UILabel *CurrentDistance;
- (IBAction)stopActivity:(id)sender;
#property(nonatomic, strong) MKPolyline *polyline;
#property (nonatomic,strong) NSMutableArray *locations;
#property (nonatomic,strong) NSNumber *currentSpeed;
#property (nonatomic,strong) NSMutableArray *locationHistory;
- (void) stopLocationManager;
#end

Archiving and Unarchiving data/model objects

I'm following the MVCS paradigm for saving and then loading instances of data objects in a Test app related to updating mapping data.
BNRMapPoint.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#import <CoreLocation/CoreLocation.h>
#interface BNRMapPoint : NSObject<MKAnnotation, NSCoding>
{
double latitude;
double longitude;
#public NSArray *mapPoints;
}
-(id) initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t subTitle:(NSString *)st;
//this is required property in MKAnnotation Protocol
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
//this is an optional property in MKAnnotation Protocol
#property (nonatomic, copy) NSString *title;
//this is an optional property in MKAnnotation Protocol
#property (nonatomic,readonly,copy) NSString *subTitle;
#end
BNRMapPoint.m (Model)
#import "BNRMapPoint.h"
#implementation BNRMapPoint
#synthesize coordinate=_coordinate;
#synthesize title=_title;
#synthesize subtitle=_subtitle;
-(void)encodeWithCoder:(NSCoder *)aCoder{
latitude = self.coordinate.latitude;
longitude = self.coordinate.longitude;
[aCoder encodeObject:self.title forKey:#"title"];
[aCoder encodeObject:_subTitle forKey:#"subTitle"];
[aCoder encodeDouble: latitude forKey:#"latitude"];
[aCoder encodeDouble:longitude forKey:#"longitude"];
}
-(id)initWithCoder:(NSCoder *)aDecoder{
if (self= [super init]) {
[self setTitle:[aDecoder decodeObjectForKey:#"title"]];
self->_subTitle= [aDecoder decodeObjectForKey:#"subTitle"];
latitude= [aDecoder decodeDoubleForKey:#"latitude"];
longitude= [aDecoder decodeDoubleForKey:#"longitude"];
}
return self;
}
-(id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t subTitle:(NSString *)st{
if (self= [super init]) {
self.coordinate=c;
self.title=t;
_subtitle=st;
}
return self;
}
-(id) init{
return [self initWithCoordinate:CLLocationCoordinate2DMake(43.07, -89.32) title:#"Hometown" subTitle:self.subtitle];
}
#end
WhereamiViewController.h
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>
#import "BNRMapPoint.h"
#import "RootObject.h"
#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
WhereamiViewController.m (ViewController)
#import "WhereamiViewController.h"
#interface WhereamiViewController ()
#end
#implementation WhereamiViewController
-(IBAction)buttonDidGetPressed:(UISegmentedControl *)sender{//Silver challenge
NSLog(#"%#",NSStringFromSelector(_cmd));
if([sender selectedSegmentIndex]==0){
[worldView setMapType:MKMapTypeStandard];
}
else if([sender selectedSegmentIndex]==1){
[worldView setMapType:MKMapTypeHybrid];
}
else if([sender selectedSegmentIndex]==2){
[worldView setMapType:MKMapTypeSatellite];
}
}
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
NSLog(#"%#", NSStringFromSelector(_cmd));
if (self=[super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
rootObj= [[RootObject alloc] init];
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];
}
NSLog(#"test");
return self;
}
-(void) viewDidLoad{
// [worldView setMapType:MKMapTypeSatellite]; Bronze challenge
[worldView setShowsUserLocation:YES];
}
-(void)findLocation{
NSLog(#"%#",NSStringFromSelector(_cmd));
[locationManager startUpdatingLocation];//This calls locationManager:didUpdateLocations:
NSLog(#"location updated");
[activityIndicator startAnimating];
[locationTitleField setHidden:YES];
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
NSLog(#"%#",NSStringFromSelector(_cmd));
[self findLocation];
[textField resignFirstResponder];
return YES;
}
-(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation{
NSLog(#"%#",NSStringFromSelector(_cmd));
CLLocationCoordinate2D centerCoordinate= [[userLocation location] coordinate]; //get the coordinate of current location.
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];
}
-(void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading{
NSLog(#"%#",NSStringFromSelector(_cmd));
NSLog(#"Heading %#",newHeading);
}
-(void)foundLocation:(CLLocation *)loc{
NSLog(#"%#",NSStringFromSelector(_cmd));
CLLocationCoordinate2D coord= [loc coordinate];
NSDateFormatter *formatter= [[NSDateFormatter alloc] init];
NSDate *currentDate= [[NSDate alloc] init];
[formatter setDefaultDate:currentDate];
BNRMapPoint *bmp= [[BNRMapPoint alloc] initWithCoordinate:coord title:[locationTitleField text] subTitle:[[formatter defaultDate] description]];
[rootObj.mapPoints addObject:bmp];
[worldView addAnnotation:bmp];
MKCoordinateRegion region= MKCoordinateRegionMakeWithDistance(coord,250,250);
[worldView setRegion:region animated:YES];
//RESET the UI
[locationTitleField setText:#" "];
[activityIndicator stopAnimating];
[locationTitleField setHidden:NO];
[locationManager stopUpdatingLocation];
}
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{ //CLLocationManagerDelegate method implementation
NSLog(#"%#", NSStringFromSelector(_cmd));
// NSTimeInterval t0=[[locations lastObject] timeIntervalSinceNow];
NSLog(#"%#",(CLLocation *)[locations lastObject]);
NSTimeInterval t= [[(CLLocation *)[locations lastObject] timestamp] timeIntervalSinceNow];
if (t<-180) {
return; //No op
}
[self foundLocation:[locations lastObject]];
}
-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error{
NSLog(#"Could not find location: %#",error);//CLLocationManagerDelegate method implementation
}
#end
RootObject.h
#import <Foundation/Foundation.h>
#interface RootObject : NSObject
#property NSMutableArray *mapPoints;
-(BOOL)saveChanges;
-(NSString *)dataObjectArchivePath;
#end
RootObject.m (Store)
#import "RootObject.h"
#implementation RootObject
#synthesize mapPoints;
-(NSString *)dataObjectArchivePath{
NSArray *documentDirectories= NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory= [documentDirectories objectAtIndex:0];
NSString *finalPath= [documentDirectory stringByAppendingPathComponent:#"items.archive"];
return finalPath;
}
-(BOOL)saveChanges{
NSString *path= [self dataObjectArchivePath];
return [NSKeyedArchiver archiveRootObject:self.mapPoints toFile:path];writing to the file items.archive
}
-(id)init{
if (self=[super init]) {
NSString *path= [self dataObjectArchivePath];
self.mapPoints= [NSKeyedUnarchiver unarchiveObjectWithFile:path];//reading from the file items.archive
if (!self.mapPoints) {
self.mapPoints= [[NSMutableArray alloc] init];
}
}
return self;
}
#end
AppDelegate.m
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
NSLog(#"%#",NSStringFromSelector(_cmd));
BOOL success= [[BNRItemStore sharedStore] saveChanges];
if (success) {
NSLog(#"Saved all of the BNRItems");
}
else{
NSLog(#"Could not save any of the BNRItems");
}
}
In the above code, the saveChanges method is called from application: didEnterBackground:. So i haven't made a singleton object for the Store (RootObject). But i instantiated it in the designated initializer of viewController. The data to be archived is BNRMapPoint objects. On the entering background state, the code did save the objects successfully. However after the relaunching the app, the initWithCoder is where its getting stuck. The app crashes when it comes to reading the previously saved MapPoint objects. Pls help.. I've checked almost possible. Don't know what to do. I'm stuck.
So the problem is that you have:
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
And you are trying to use setter on read only property:
self.coordinate=c;
This is why it says that unrecognized selector is send - becuase it's readonly so setter doesn't exists. Using property this was is the same as calling:
[self setCoordinate:c]
Just make property assignable

MKAnnotationView update title and subtitle with distance when event is received

I'm developing an iphone app using mapkit and CLLocationManager.
I put lots of MKPinAnnotationView on map (about 100) and I want to update all callout's subtitle whith user distance when I receive it.
How to do it ?
Thanks
I try this to update subtitle callout with new location but it didn't work well.
In my MyAppDelegate.h
extern NSString * const GMAP_USERLOCATION_CHANGED;
#interface MyAppDelegate : NSObject <UIApplicationDelegate> {
CLLocationManager *locationManager;
CLLocation *userLocation;
}
#property (nonatomic, retain) CLLocationManager *locationManager;
#property (nonatomic, retain) CLLocation *userLocation;
#end
In my MyAppDelegate.m
#implementation MyAppDelegate
NSString * const GMAP_USERLOCATION_CHANGED = #"gMapUserLocationChanged";
#synthesize locationManager, userLocation;
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
userLocation = nil;
[[self locationManager] startUpdatingLocation];
[window addSubview:tabBarController.view];
[window makeKeyAndVisible];
}
#pragma mark -
#pragma mark Core Location delegate
- (CLLocationManager *)locationManager
{
if (locationManager != nil)
{
return locationManager;
}
locationManager = [[CLLocationManager alloc] init];
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
locationManager.delegate = self;
return locationManager;
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
self.userLocation = newLocation;
// send notification to defaulcenter
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc postNotificationName:GMAP_USERLOCATION_CHANGED object:self.userLocation];
}
- (void)dealloc {
[window release];
[locationManager release];
[userLocation release];
[super dealloc];
}
#end
I made a customAnnotationView named MyAnnotation.
in MyAnnotation.h :
#interface MyAnnotation : MKPinAnnotationView<MKAnnotation>
{
double longitude;
double latitude;
NSString *title;
NSString *subtitle;
}
#property (nonatomic, retain) NSString *title;
#property (nonatomic, retain) NSString *subtitle;
#property double longitude;
#property double latitude;
#end
in MyAnnotation.m :
#import "MyAnnotation.h"
#import "MyAppDelegate.h"
#implementation MyAnnotation
#synthesize title, subtitle;
#synthesize longitude;
#synthesize latitude;
-(id)initWithAnnotation:(id <MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
if (self != nil)
{
NSLog(#"add observer");
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:#selector(receivedNewUserLocation:) name:GMAP_USERLOCATION_CHANGED object:nil];
}
return self;
}
- (void)setTitleAndSubtitle
{
[self setTitleAndSubtitle:nil];
}
- (id)setTitleAndSubtitle:(CLLocation*)userLocation
{
CLLocationDistance dist = -1;
if(userLocation)
{
CLLocation *poiLoc = [[CLLocation alloc] initWithLatitude:self.latitude longitude:self.longitude];
dist = [userLocation distanceFromLocation:poiLoc] / 1000;
NSLog(#"distance is now %.f", dist);
}
title = #"the Title of the poi!";
subtitle = [NSString stringWithFormat:#"Distance: %#",
dist > -1 ? [NSString stringWithFormat:#"%.2f km", dist] : #"-"
];
return self;
}
- (void)receivedNewUserLocation:(NSNotification *)userLocationNotification
{
CLLocation *userlocation = (CLLocation*)[userLocationNotification object];
[self setTitleAndSubtitle:userlocation];
}
- (CLLocationCoordinate2D)coordinate;
{
CLLocationCoordinate2D theCoordinate;
theCoordinate.latitude = latitude;
theCoordinate.longitude = longitude;
return theCoordinate;
}
- (NSString *)title
{
return title;
}
- (NSString *)subtitle
{
return subtitle;
}
- (void)dealloc
{
[title release];
[subtitle release];
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc removeObserver:self];
[super dealloc];
}
#end
In the end I use it like that in my MapViewController (I put only the viewForAnnotation delegate method here):
- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id <MKAnnotation>)annotation
{
// if it's the user location, just return nil.
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
MyAnnotation *annotationView = nil;
MyAnnotation* myAnnotation = (MyAnnotation *)annotation;
// try to dequeue an existing pin view first
NSString* identifier = #"CustomMapAnnotation";
MyAnnotation *customPinView = (MyAnnotation *)[self.mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if(nil == customPinView)
{
// if an existing pin view was not available, create one
customPinView = [[[MyAnnotation alloc]
initWithAnnotation:myAnnotation
reuseIdentifier:identifier]
autorelease];
customPinView.animatesDrop = YES;
customPinView.canShowCallout = YES;
}
annotationView = customPinView;
[annotationView setEnabled:YES];
[annotationView setCanShowCallout:YES];
return annotationView;
}
After all of this subtitle is not update after the mkannotation is load on map....
What's wrong ?
thanks for your help...
When you update the title, you should notify the MKAnnotationView to update the callout view, by whichever KVO manner that fits your need best, e.g.:
synthesize or implement setter for title and use
self.title = #"new title";
use explicit KVO notifications
[self willChangeValueForKey:#"title"];
[title release];
title = [newTitle copy];
[self didChangeValueForKey:#"title"];
Ditto for subtitle.
It depends on how you are creating your MKAnnotations.
You should probably have an object like "Place" represented by Place.h and Place.m, which conform to the MKAnnotation protocol...
Place would have a property along the lines of
float distance;
Then your subtitle method (part of MKAnnotation) would do something like this
- (NSString *)subtitle
{
return [NSString stringWithFormat:#"%0.2f Miles Away", distance];
}
That subtitle method gets called by the mapview constantly (in fact its almost ridiculous how often it gets called), so as soon as you manipulate the value of distance, it will be reflected on the map (perhaps as early as the next time you tap on the pin).