I am having little bit confused regarding this way of asking question
Any how i am mentioning my query with support of image here. Can you please help me out regarding this issue
Step 1: I have an requirement like: By using CLLocationManger delegate methods i fetched speed value like:
- (void)startLocationUpdates{
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
locationManager.pausesLocationUpdatesAutomatically = YES;
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
[locationManager requestWhenInUseAuthorization];
[locationManager startUpdatingLocation];
}
#pragma mark
#pragma mark - CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations{
float speed = locationManager.location.speed;
float speedInKmph = speed * 3.6; // to convert the speed into kmph.
NSString *speedValue =[NSString stringWithFormat:#"%.f Kmph ",speedInKmph];
self.currentSpeedLblRef.text = speedValue;
self.maxSpeedLblRef.text = speedValue;
}
Step 2: currentSpeedLblRef,maxSpeedLblRef --> these are my UILabels
Example: Now i am driving a car --> For the first time i opened the app and i got the current speed of car(like: 120 Kmph) and then i need to display same value in "maxSpeedLblRef"(120 Kmph) also
After some time my current speed of car if 50 Kmph. But i need to display value in "maxSpeedLblRef" is --> Max value --> means 120 kmph . Because already i was getting 120 kmph value for previos
After that If my current speed of car if 180 kmph --> I need to show "maxSpeedLblRef" valu like: 180 Kmph. Because it is the latest one compare to 120 Kmph
After Close the app and then
If i reopen the app i want to show the vale like "maxSpeedLblRef" --> 180 Kmph. Because this value is the previous saved value
HERE IS MY SOURCE CODE:Click here link
you are missing a few simple things here!
You need to store the max speed for comparison - all you're doing here is is updating the label with the current value every time. Set up a class-level attribute, initial value = 0, and update it whenever current speed > max speed.
There are a few ways you can store the max value so that it's there when you next open the app. Probably easiest to go with user defaults. There are many tutorials available - try this one http://code.tutsplus.com/tutorials/ios-sdk-working-with-nsuserdefaults--mobile-6039
OK - using your project code, here's what you need.
Update your ViewController.h to this
// ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *currentSpeedLblRef;
#property (weak, nonatomic) IBOutlet UILabel *maxSpeedLblRef;
// you need to store the max speed
#property float speedMax;
-(void)loadDefaults;
-(void)storeDefaults;
#end
and then in the VIewController.m, replace the viewDidLoad with this
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self loadDefaults];
[self startLocationUpdates];
}
update the locationManager function to this
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations{
float speed = locationManager.location.speed;
float speedInKmph = speed * 3.6; // to convert the speed into kmph.
NSString *speedValue =[NSString stringWithFormat:#"%.f Kmph ",speedInKmph];
self.currentSpeedLblRef.text = speedValue;
if (speedInKmph > self.speedMax)
{
self.speedMax = speedInKmph;
[self storeDefaults];
}
NSString *speedMaxValue =[NSString stringWithFormat:#"%.f Kmph ",self.speedMax];
self.maxSpeedLblRef.text = speedMaxValue;
}
and, finally add the load / store functions
- (void)loadDefaults
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
self.speedMax = [defaults floatForKey:#"SpeedMax"];
}
- (void)storeDefaults
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setFloat:self.speedMax forKey:#"SpeedMax"];
[defaults synchronize];
}
Related
Okay, this is an odd one - and the question has been asked before in various guises and always closed out with the opinion that a) it won't be accurate and b) that it will get less accurate with time.
I understand this - but I'm doing an experiment to see whether that accuracy can be boosted at all by taking into account other evidential sources (for example, if a map has been plotted in advance then the direction of travel from the compass combined with the route could provide another evidence source).
The problem is that my code is clearly rubbish - so I'd welcome your opinion. I suspect that this might be a brown paper bag error!
My ViewController.h looks like this:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UIAccelerometerDelegate> {
UIAccelerometer *accelerometer;
long last_speed;
long distance_travelled;
long lastAccel;
long long lastTime;
IBOutlet UITextField* speedView;
IBOutlet UITextField* distanceView;
}
#end
And my ViewController.m looks like this:
#import "ViewController.h"
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
lastAccel = 0;
last_speed = 0;
distance_travelled = 0;
lastTime = (long)(NSTimeInterval)([NSDate.date timeIntervalSince1970] * 1000.0);
accelerometer = UIAccelerometer.sharedAccelerometer;
accelerometer.updateInterval = 0.1;
accelerometer.delegate = self;
}
- (void)accelerometer:(UIAccelerometer *)meter
didAccelerate:(UIAcceleration *)acceleration
{
long long currentTime = (long)(NSTimeInterval)([NSDate.date timeIntervalSince1970] * 1000.0);
long long deltaTime = currentTime - lastTime;
lastTime = currentTime;
long accel_x = acceleration.x;
long accel_y = acceleration.y;
long accel_z = acceleration.z;
long integrated_acceleration = sqrtl(accel_x*accel_x + accel_y*accel_y + accel_z*accel_z);
long average_acceleration = (lastAccel + integrated_acceleration) / 2;
long speed = last_speed + (average_acceleration * deltaTime);
long average_speed = (speed + last_speed) / 2;
distance_travelled = distance_travelled + (average_speed * deltaTime);
last_speed = speed;
lastAccel = integrated_acceleration;
[speedView setText:[NSString stringWithFormat:#"%ld", speed]];
[distanceView setText:[NSString stringWithFormat:#"%ld", distance_travelled]];
}
#end
When the code is run, the speed and the distance keep going up continually, and deceleration (me slowing down) is never taken into account - so even when I stop, speed and distance keep going up.
Even by the standards of 'this will be a bit inaccurate' that's taking it too far!
All thoughts and opinions gratefully received (well almost - I know that it won't be accurate!)
You can't calculate Speed using UIAccelerometer.
Please find the link for more clarification : How to calculate speed using accelerometer in ios
I am creating a simple drum machine. This function controls the time between each sample that is played (thus controlling the tempo of the drum machine). I need to control the tempo with a slider, so I'm hoping to be able to control the 'time duration until next step' value with this if possible. However, when I have tried to do this, it tells me "time is part of NSDate"
-(void)run
{
#autoreleasepool
{
// get current time
NSDate* time = [NSDate date];
// keeping going around the while loop if the sequencer is running
while (self.running)
{
// sleep until the next step is due
[NSThread sleepUntilDate:time];
// update step
int step = self.step + 1;
// wrap around if we reached NUMSTEPS
if (step >= NUMSTEPS)
step = 0;
// store
self.step = step;
// time duration until next step
time = [time dateByAddingTimeInterval:0.5];
}
// exit thread
[NSThread exit];
}
}
This tells me NSTimeInterval is an incompatable type
// time duration until next step
time = [time dateByAddingTimeInterval: self.tempoControls];
Here is where the slider is declared
.m
- (IBAction)sliderMoved:(UISlider *)sender
{
AppDelegate* app = [[UIApplication sharedApplication] delegate];
if (sender == self.tempoSlider)
{
PAEControl* tempoControl = app.tempoControls[app.editIndex];
tempoControl.value = self.tempoSlider.value;
}
}
.h
#interface DetailController : UIViewController
#property (weak, nonatomic) IBOutlet UISlider *tempoSlider;
- (IBAction)sliderMoved:(UISlider *)sender;
Any help would me much appriciated, thanks in advance.
It looks like self.tempoControls is an array of PAEControl objects. The method named dateByAddingTimeInterval: needs an argument of type NSTimeInterval (aka double). It looks like you're trying to pass in this array instead.
Try changing this line -
time = [time dateByAddingTimeInterval: self.tempoControls];
To maybe this -
PAEControl* tempoControl = self.tempoControls[self.editIndex];
time = [time dateByAddingTimeInterval: (NSTimeInterval)tempoControl.value];
On another note, if this is all running on the main thread, be aware that you are blocking it and the UI will become very unresponsive.
As my user changes location, I need to compare his location with an array of locations I pull from the web. However, in my appDelegate, I'm not sure where exactly to place my code as I'm not sure what methods are called or not called when the app is terminated, but the CLLocationManager still works.
Specifically, I need to input this code where it will actually be called when the app is still terminated:
// alloc and init the various (Mutable)Array properties
self.locations = [[NSMutableArray alloc] init];
// Create new HomeModel object and assign it to _homeModel variable
_homeModel = [[DealsModel alloc] init];
// Set this view controller object as the delegate for the home model object
_homeModel.delegate = self;
// Call the download items method of the home model object
[_homeModel downloadItems];
The _homeModel will then call this method:
-(void)itemsDownloaded:(NSArray *)items
{
// This delegate method will get called when the items are finished downloading
// Set the downloaded items to the array
_locations = [items copy];
}
Which I will further edit to compare the user's location to the array of locations.
The thing is, this array of locations only changes once a week. Does the app really have to pull it from the web every time the user's location changes? Or is there a way to cache this and only pull it when self.locations has been deallocated?
This is what I have now, but I feel there must be a better way:
#interface AppDelegate () <CLLocationManagerDelegate>
{
DealsModel *_homeModel;
}
#property BOOL didRunBefore;
#property CLLocationManager *locationManager;
#property NSMutableArray *deals;
#end
#implementation AppDelegate
-(void)itemsDownloaded:(NSArray *)items
{
// This delegate method will get called when the items are finished downloading
// Set the downloaded items to the array
_deals = [items copy];
[self compareSponsorLocations:_deals toUserLocation:[self.locationManager location]];
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations {
if (!self.deals) {
// alloc and init the various (Mutable)Array properties
self.deals = [[NSMutableArray alloc] init];
// Create new HomeModel object and assign it to _homeModel variable
_homeModel = [[DealsModel alloc] init];
// Set this view controller object as the delegate for the home model object
_homeModel.delegate = self;
// Call the download items method of the home model object
[_homeModel downloadItems];
} else {
[self compareSponsorLocations:self.deals toUserLocation:[locations lastObject]];
}
}
- (void) compareSponsorLocations: (NSArray *) array toUserLocation: (CLLocation *) location
{
for (Deal *deal in array) {
NSLog(#"%#", deal.name);
}
NSLog(#"%#", location.description);
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
...
To handle location updates when you app is in background or even terminated, you can use "Significant Location Changes". It will trigger application to start in background mode when location has been changed significantly. Then you can start a background task to perform the operations on your need.
Documentation:
https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/LocationAwarenessPG/CoreLocation/CoreLocation.html#//apple_ref/doc/uid/TP40009497-CH2-SW8
Easy way to simulate it and debug:
XCode / iOS simulator: Trigger significant location change manually
I want to call a method from elsewhere in the app to get the user's location that was obtained in the app delegate. When calling CLLocation *getCurLoc = [AppDelegate getCurrentLocation]; from another view controller, nothing is returned.
The App Delegate is,
#synthesize locationManager;
CLLocation *location;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
locationManager = [[CLLocationManager alloc]init];
locationManager.delegate = self;
locationManager.desiredAccuracy=kCLLocationAccuracyKilometer;
[locationManager startUpdatingLocation];
return YES;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
[locations lastObject];
[manager stopUpdatingLocation];
CLLocation *location = [locations lastObject];
}
+(CLLocation *)getCurrentLocation{
return location;
}
Changing it to an instance method with a "-" didn't work. Should "location" be made into an instance? Should the delegate be made into an instance, or is there a better way to access it from elsewhere?
In didUpdateLocations, you define and set a local variable location, not the global variable location defined at the top of the file. Changing the line
CLLocation *location = [locations lastObject];
to
location = [locations lastObject];
would fix the problem. But the better solution is to use a property in the AppDelegate class.
You can define it in a class extension:
#interface AppDelegate ()
#property(strong, nonatomic) CLLocation *location;
#end
Then you access it in instance methods like
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
[locations lastObject];
[manager stopUpdatingLocation];
self.location = [locations lastObject];
}
and in a class method like
+(CLLocation *)getCurrentLocation{
// Get the instance:
AppDelegate *app = [[UIApplication sharedApplication] delegate];
return app.location;
}
Update: If the AppDelegate is declared to conform so some protocol (in the
public interface or in the class extension), for example:
#interface AppDelegate () <CLLocationManagerDelegate>
#property(strong, nonatomic) CLLocation *location;
#end
then the above code creates a warning
initializing 'AppDelegate *__strong' with an expression of incompatible type 'id<UIApplicationDelegate>'
and an explicit cast is necessary:
+(CLLocation *)getCurrentLocation{
// Get the instance:
AppDelegate *app = (AppDelegate *)[[UIApplication sharedApplication] delegate];
return app.location;
}
Martin's answer is an effective quick&dirty way of solving this problem in a way that is consistent with your approach - storing the location in appDelegate.
If you want to take a step further you might want to consider implementing a special object that would hold the data - the data model. It is considered a bad practice to store data in application delegate - it is not what it is there for (though it works perfectly fine in sample or small applications).
You could do something like this:
DataModel.h
#import <Foundation/Foundation.h>
#interface DataModel : NSObject
#property (strong) CLLocation *location;
+ (DataModel *)sharedModel;
#end
DataModel.m
#import "DataModel.h"
#class CLLocation;
#implementation DataModel
- (id) init
{
self = [super init];
if (self)
{
_location = nil;
}
return self;
}
+ (DataModel *)sharedModel
{
static DataModel *_sharedModel = nil;
static dispatch_once_t onceSecurePredicate;
dispatch_once(&onceSecurePredicate,^
{
_sharedModel = [[self alloc] init];
});
return _sharedModel;
}
#end
You would then need to #import "DataModel.h" wherever you need it. You would change your didUpdateLocations: to:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
[locations lastObject];
[manager stopUpdatingLocation];
[DataModel sharedInstance].location = [locations lastObject];
}
And from anywhere in the code you could get this location simply by [DataModel sharedInstance].location.
EDIT:
For a very simple app this approach might look as an overkill. But as soon as your app grows it surely pays off to use it.
This kind of class/object/singleton is ment to hold all the data your app needs (fixed and temporary). So all the data sources can make a good use of it. In short: it enables you to easily follow the model-view-controller guidelines.
You cold of course use another class/object/singleton to hold the temporary data - it depends on the complexity of your data-structure.
You don't have to specifically initialize this kind of object. It is initialized the first time you reference it. That is why dispatch_once is there for. It makes sure that there is one and only one instance of this shared object present: https://stackoverflow.com/a/9119089/653513
And this one single instance of [DataModel sharedInstance] will remain there until your app is terminated.
Apple uses similar approach for [NSUserDefaults standardDefaults], [UIApplication sharedApplicaton] for example.
I tend to put the #import "DataModel.h" into my projects Prefix.pch so I don't have to import it every single time I use it (it is used trough all the app).
PROS:
- data accesible throughout the app
- code reusability
- MVC structure
- code is more readable
CONS:
- I couldn't really find one. Except that the dispatch_once(&onceSecurePredicate,^... might confuse one for the first couple of seconds.
You can access the AppDelegate from any point in your application using [UIApplication sharedApplication].delegate so you could do:
CLLocation *location = [(YourAppDelegateClassName*)[UIApplication sharedApplication].delegate getCurrentLocation];
The method getCurrentLocation needs to be an instance method (-(CLLocation *)getCurrentLocation). You will need also to import #import "YourAppDelegateClassName.h" in those files you need to use that method.
To avoid the casting and accessing [UIApplication sharedApplication].delegate everytime I prefer to implement a static method in my AppDelegates:
+ (YourAppDelegateClassName*)sharedDelegate {
return (YourAppDelegateClassName*)[UIApplication sharedApplication].delegate;
}
So you can use any method like this:
CLLocation *location = [[YourAppDelegateClassName sharedDelegate] getCurrentLocation];
So I've developed a standalone compass app (arrow revolving to point tofixed lat/long point) which works perfectly as a standalone project but when I've come to incorporate it into the wider project I get a problem.
Initially I get a semantic warning (Incompatible pointer types assigning to 'CLLocationManager*' from 'CLLoccation *__strong') for:
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
if (newLocation.horizontalAccuracy >= 0) {
self.recentLocation = newLocation; (WARNING HERE)
CLLocation *POI2location = [[CLLocation alloc]
initWithLatitude:kPOI2Latitude
longitude:kPOI2Longitude];
CLLocationDistance delta = [POI2location
distanceFromLocation:newLocation];
And further on I get a fatal error (Property 'coordinate' not found on object of type 'CLLocationManager) for:
- (void)locationManager:(CLLocationManager *)manager
didUpdateHeading:(CLHeading *)newHeading {
if (self.recentLocation != nil && newHeading.headingAccuracy >= 0) {
CLLocation *POI2Location = [[CLLocation alloc]
initWithLatitude:kPOI2Latitude
longitude:kPOI2Longitude];
double course = [self headingToLocation:POI2Location.coordinate
current:recentLocation.coordinate]; (WARNING HERE)
For some reason it doesn't like 'recentLocation' now whereas it was all working perfectly before. Can someone point out to me what I'm missing. I'm sure it's obvious to someone with more experience than me.
Many thanks in advance.
Started again this morning and the answer is staring me in the face!
In the .h file i'd put
#property (strong, nonatomic) CLLocationManager *recentLocation;
when I should have put
#property (strong, nonatomic) CLLocation *recentLocation;