#import "CPPedometerViewController.h"
#import <CoreMotion/CoreMotion.h>
#interface CPPedometerViewController ()
#property (weak, nonatomic) IBOutlet UILabel *stepsCountingLabel;
#property (nonatomic, strong) CMStepCounter *cmStepCounter;
#property (nonatomic, strong) NSOperationQueue *operationQueue;
#property (nonatomic, strong) NSMutableArray *stepsArray;
#end
#implementation CPPedometerViewController
- (NSOperationQueue *)operationQueue
{
if (_operationQueue == nil)
{
_operationQueue = [NSOperationQueue new];
}
return _operationQueue;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self QueryExistingStep];
NSLog( #"steps array = %#", _stepsArray);
}
-(void)QueryExistingStep
{
//get todays date
NSDate *now = [NSDate date];
// get six days ago from today
NSDate *sixDaysAgo = [now dateByAddingTimeInterval:-6*24*60*60];
//array to hold step values
_stepsArray = [[NSMutableArray alloc] initWithCapacity:7];
//check if step counting is avaliable
if ([CMStepCounter isStepCountingAvailable])
{
//init step counter
self.cmStepCounter = [[CMStepCounter alloc] init];
//get seven days before from date & to date.
for (NSDate *toDate = [sixDaysAgo copy]; [toDate compare: now] <= 0;
toDate = [toDate dateByAddingTimeInterval:24 * 60 * 60] ) {
//get day before
NSDate *fromDate = [[toDate copy] dateByAddingTimeInterval: -1 * 24 * 60 * 60];
[self.cmStepCounter queryStepCountStartingFrom:fromDate to:toDate toQueue:self.operationQueue withHandler:^(NSInteger numberOfSteps, NSError *error) {
if (!error) {
NSLog(#"queryStepCount returned %ld steps", (long)numberOfSteps);
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self updateArrayWithStepCounter:numberOfSteps];
}];
} else {
NSLog(#"Error occured: %#", error.localizedDescription);
}
}];
}
} else {
// stuffhappens
}
}
- (void)updateArrayWithStepCounter:(NSInteger)numberOfSteps {
[_stepsArray addObject:[NSNumber numberWithInteger:numberOfSteps]];
}
#end
I'm looking to have an array full of steps from the past seven days, and then insert them into to a NSinteger for each day. e.g. NSinteger daySeven = 242, NSInteger daySix = 823 ... etc too today.
However the array seems to clear after exiting the updateArrayWithStepCounter method. Any idea on how i could fix this so each number of steps goes into separate NSIntegers also. Thanks, Ryan.
EDIT:
Here is the NSLog output:
2014-01-25 22:51:36.314 Project[6633:60b] steps array = (
)
2014-01-25 22:51:36.332 Project[6633:420f] queryStepCount returned 3505 steps
2014-01-25 22:51:36.334 Project[6633:420f] queryStepCount returned 3365 steps
2014-01-25 22:51:36.335 Project[6633:420f] queryStepCount returned 7206 steps
2014-01-25 22:51:36.337 Project[6633:420f] queryStepCount returned 6045 steps
2014-01-25 22:51:36.339 Project[6633:420f] queryStepCount returned 5259 steps
2014-01-25 22:51:36.342 Project[6633:420f] queryStepCount returned 6723 steps
2014-01-25 22:51:36.344 Project[6633:420f] queryStepCount returned 440 steps
Here is the output shown as suggested. As you can see its definitely getting the values however when it checks the array after running the method its now empty.
Could i be adding it to the array incorrectly?
I hope this is clearer I'm stumped. Thanks
First, what's the output of this?
NSLog(#"steps array = %#", _stepsArray);
When someone ask you something, you should try to reply with the exact information requested, just saying "it says that array is empty" doesn't help, because maybe someone can see something that you don't see in the output.
Said this, I would add some more NSLog around, because it could be that your handler is not called, or not called with the information that you expect.
Use the following, and let us know the output :)
[self.cmStepCounter queryStepCountStartingFrom:fromDate to:toDate toQueue:self.operationQueue withHandler:^(NSInteger numberOfSteps, NSError *error) {
if (!error) {
NSLog(#"queryStepCount returned %d steps", numberOfSteps);
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self updateArrayWithStepCounter:numberOfSteps];
}];
} else {
NSLog(#"Error occured: %#", error.localizedDescription);
}
}];
EDIT: From the new posted output of the NSLog, I can understand the problem. The fact is that the handler runs asynchronously, it means that you can't just output the array on viewDidLoad, because it runs BEFORE the array received all the values, so you should refactor your code to trigger a method when all the data is ready.
Here a revision of your code that is more readable (removed some useless "copy" call, updated your "for conditions", etc...), now it should be really easy to understand what's going on, and how to perform additional logic.
#import "PYViewController.h"
#import <CoreMotion/CoreMotion.h>
#interface PYViewController ()
#property (weak, nonatomic) IBOutlet UILabel *stepsCountingLabel;
#property (nonatomic, strong) CMStepCounter *cmStepCounter;
#property (nonatomic, strong) NSOperationQueue *operationQueue;
#property (nonatomic, strong) NSMutableArray *stepsArray;
#end
#implementation PYViewController
- (NSOperationQueue *)operationQueue {
if (_operationQueue == nil) {
_operationQueue = [NSOperationQueue new];
_operationQueue.maxConcurrentOperationCount = 1; // process 1 operation at a time, or we could end with unexpected results on _stepsArray
}
return _operationQueue;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self queryExistingStep];
}
-(void)queryExistingStep {
// Get now date
NSDate *now = [NSDate date];
// Array to hold step values
_stepsArray = [[NSMutableArray alloc] initWithCapacity:7];
// Check if step counting is avaliable
if ([CMStepCounter isStepCountingAvailable]) {
// Init step counter
self.cmStepCounter = [[CMStepCounter alloc] init];
// Tweak this value as you need (you can also parametrize it)
NSInteger daysBack = 6;
for (NSInteger day = daysBack; day > 0; day--) {
NSDate *fromDate = [now dateByAddingTimeInterval: -day * 24 * 60 * 60];
NSDate *toDate = [fromDate dateByAddingTimeInterval:24 * 60 * 60];
[self.cmStepCounter queryStepCountStartingFrom:fromDate to:toDate toQueue:self.operationQueue withHandler:^(NSInteger numberOfSteps, NSError *error) {
if (!error) {
NSLog(#"queryStepCount returned %ld steps", (long)numberOfSteps);
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[_stepsArray addObject:#(numberOfSteps)];
if ( day == 1) { // Just reached the last element, do what you want with the data
NSLog(#"_stepsArray filled with data: %#", _stepsArray);
// [self updateMyUI];
}
}];
} else {
NSLog(#"Error occured: %#", error.localizedDescription);
}
}];
}
} else {
NSLog(#"device not supported");
}
}
#end
What i would do is waiting for the array to get all the needed value and do stuff later. It works for me.
- (void)queryPast6DayStepCounts
{
NSLog(#"queryPast6DayStepCounts visited!");
self.past6DaysDailySteps = [[NSMutableArray alloc] initWithCapacity:6];
// past 6 days
for (NSInteger day = 6; day >= 1; day--)
{
NSDate *fromDate = [self.todayMidnight dateByAddingTimeInterval:-day * 24 * 60 * 60];
NSDate *toDate = [fromDate dateByAddingTimeInterval:24 * 60 * 60];
[self.cmStepCounter
queryStepCountStartingFrom:fromDate to:toDate
toQueue:[NSOperationQueue mainQueue]
withHandler:^(NSInteger numberOfSteps, NSError *error)
{
if (!error)
{
[self.past6DaysDailySteps addObject:#(numberOfSteps)];
if (self.past6DaysDailySteps.count == 6) {
[self viewDidFinishQueryPast6DaysData];
}
}
else
{
NSLog(#"Error occured: %#", error.localizedDescription);
}
}
];
}
}
-(void)viewDidFinishQueryPast6DaysData
{
NSLog(#"queryPast6DayStepCounts finish! %#", self.past6DaysDailySteps);
// do other things
}
Related
I've found very limited resources on this topic (CMPedometer). I was wondering if anyone here has managed to get this to work properly. My code is fairly simple, and has more than what I'm trying to do. Basically, the step counter does not increment EVERY step a user takes.
It actually is tracking every step the user takes but it updates so slowly and I can't figure out why. I even tried using NSTimer to make a request to update the labels every half a second. I want to try to get the step counter to update as a user takes a step. Here is my code...
#import "ViewController.h"
#import <CoreMotion/CoreMotion.h>
#interface ViewController ()
#property (nonatomic, strong) CMPedometer *pedometer;
#property (nonatomic, weak) IBOutlet UILabel *startDateLabel;
#property (nonatomic, weak) IBOutlet UILabel *endDateLabel;
#property (nonatomic, weak) IBOutlet UILabel *stepsLabel;
#property (nonatomic, weak) IBOutlet UILabel *distanceLabel;
#property (nonatomic, weak) IBOutlet UILabel *ascendedLabel;
#property (nonatomic, weak) IBOutlet UILabel *descendedLabel;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
if ([CMPedometer isStepCountingAvailable]) {
self.pedometer = [[CMPedometer alloc] init];
[NSTimer scheduledTimerWithTimeInterval:0.5f
target:self
selector:#selector(recursiveQuery)
userInfo:nil
repeats:YES];
} else {
NSLog(#"Nothing available");
self.startDateLabel.text = #"";
self.endDateLabel.text = #"";
self.stepsLabel.text = #"";
self.distanceLabel.text = #"";
self.ascendedLabel.text = #"";
self.descendedLabel.text = #"";
}
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.pedometer startPedometerUpdatesFromDate:[NSDate date]
withHandler:^(CMPedometerData *pedometerData, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"data:%#, error:%#", pedometerData, error);
});
}];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.pedometer stopPedometerUpdates];
}
- (NSString *)stringWithObject:(id)obj {
return [NSString stringWithFormat:#"%#", obj];
}
- (NSString *)stringForDate:(NSDate *)date {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateStyle = NSDateFormatterShortStyle;
formatter.timeStyle = NSDateFormatterShortStyle;
return [formatter stringFromDate:date];
}
- (void)queryDataFrom:(NSDate *)startDate toDate:(NSDate *)endDate {
[self.pedometer queryPedometerDataFromDate:startDate
toDate:endDate
withHandler:
^(CMPedometerData *pedometerData, NSError *error) {
NSLog(#"data:%#, error:%#", pedometerData, error);
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
NSLog(#"Error = %#",error.userInfo);
self.startDateLabel.text = #"";
self.endDateLabel.text = #"";
self.stepsLabel.text = #"";
self.distanceLabel.text = #"";
self.ascendedLabel.text = #"";
self.descendedLabel.text = #"";
} else {
self.startDateLabel.text = [self stringForDate:pedometerData.startDate];
self.endDateLabel.text = [self stringForDate:pedometerData.endDate];
self.stepsLabel.text = [self stringWithObject:pedometerData.numberOfSteps];
self.distanceLabel.text = [NSString stringWithFormat:#"%.1f[m]", [pedometerData.distance floatValue]];
self.ascendedLabel.text = [self stringWithObject:pedometerData.floorsAscended];
self.descendedLabel.text = [self stringWithObject:pedometerData.floorsDescended];
}
});
}];
}
- (void)recursiveQuery {
NSDate *to = [NSDate date];
NSDate *from = [to dateByAddingTimeInterval:-(24. * 3600.)];
[self queryDataFrom:from toDate:to];
}
Thanks in advance for any feedback!
EDIT
It seems the appropriate method to use for live updates is the following..
- (void)liveSteps {
[self.pedometer startPedometerUpdatesFromDate:[NSDate date]
withHandler:^(CMPedometerData *pedometerData, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Steps %#",pedometerData.numberOfSteps);
});
}];
}
However, even this is severely delayed. Does anyone have any idea how to use this properly to essentially update as the user takes a step?
I can only confirm your findings. I also wanted to get "true" realtime information. As it seems at this point, the API is not capable of this; even by forcing the updates into a queue, sync, async, etc.
For references and others with this question, here is the code I use based on Swift 3 and Xcode 8.2. I simply apply this portion of code in the concerned viewcontroller, after checking the CMPedometer.isStepCountingAvailable().
As you can see, I've included a small animation to update the UILabel in a more fluid manner.
// Steps update in near realtime - UILabel
self.pedoMeter.startUpdates(from: midnightOfToday) { (data: CMPedometerData?, error) -> Void in
DispatchQueue.main.async(execute: { () -> Void in
if(error == nil){
self.todaySteps.text = "\(data!.numberOfSteps)"
// Animate the changes of numbers in the UILabel
UILabel.transition(with: self.todaySteps,
duration: 0.50,
options: .transitionCrossDissolve,
animations: nil,
completion: nil)
}
})
}
I've found very limited resources on this topic (CMPedometer). I was wondering if anyone here has managed to get this to work properly. My code is fairly simple, and has more than what I'm trying to do. Basically, the step counter does not increment EVERY step a user takes.
It actually is tracking every step the user takes but it updates so slowly and I can't figure out why. I even tried using NSTimer to make a request to update the labels every half a second. I want to try to get the step counter to update as a user takes a step. Here is my code...
#import "ViewController.h"
#import <CoreMotion/CoreMotion.h>
#interface ViewController ()
#property (nonatomic, strong) CMPedometer *pedometer;
#property (nonatomic, weak) IBOutlet UILabel *startDateLabel;
#property (nonatomic, weak) IBOutlet UILabel *endDateLabel;
#property (nonatomic, weak) IBOutlet UILabel *stepsLabel;
#property (nonatomic, weak) IBOutlet UILabel *distanceLabel;
#property (nonatomic, weak) IBOutlet UILabel *ascendedLabel;
#property (nonatomic, weak) IBOutlet UILabel *descendedLabel;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
if ([CMPedometer isStepCountingAvailable]) {
self.pedometer = [[CMPedometer alloc] init];
[NSTimer scheduledTimerWithTimeInterval:0.5f
target:self
selector:#selector(recursiveQuery)
userInfo:nil
repeats:YES];
} else {
NSLog(#"Nothing available");
self.startDateLabel.text = #"";
self.endDateLabel.text = #"";
self.stepsLabel.text = #"";
self.distanceLabel.text = #"";
self.ascendedLabel.text = #"";
self.descendedLabel.text = #"";
}
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.pedometer startPedometerUpdatesFromDate:[NSDate date]
withHandler:^(CMPedometerData *pedometerData, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"data:%#, error:%#", pedometerData, error);
});
}];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.pedometer stopPedometerUpdates];
}
- (NSString *)stringWithObject:(id)obj {
return [NSString stringWithFormat:#"%#", obj];
}
- (NSString *)stringForDate:(NSDate *)date {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateStyle = NSDateFormatterShortStyle;
formatter.timeStyle = NSDateFormatterShortStyle;
return [formatter stringFromDate:date];
}
- (void)queryDataFrom:(NSDate *)startDate toDate:(NSDate *)endDate {
[self.pedometer queryPedometerDataFromDate:startDate
toDate:endDate
withHandler:
^(CMPedometerData *pedometerData, NSError *error) {
NSLog(#"data:%#, error:%#", pedometerData, error);
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
NSLog(#"Error = %#",error.userInfo);
self.startDateLabel.text = #"";
self.endDateLabel.text = #"";
self.stepsLabel.text = #"";
self.distanceLabel.text = #"";
self.ascendedLabel.text = #"";
self.descendedLabel.text = #"";
} else {
self.startDateLabel.text = [self stringForDate:pedometerData.startDate];
self.endDateLabel.text = [self stringForDate:pedometerData.endDate];
self.stepsLabel.text = [self stringWithObject:pedometerData.numberOfSteps];
self.distanceLabel.text = [NSString stringWithFormat:#"%.1f[m]", [pedometerData.distance floatValue]];
self.ascendedLabel.text = [self stringWithObject:pedometerData.floorsAscended];
self.descendedLabel.text = [self stringWithObject:pedometerData.floorsDescended];
}
});
}];
}
- (void)recursiveQuery {
NSDate *to = [NSDate date];
NSDate *from = [to dateByAddingTimeInterval:-(24. * 3600.)];
[self queryDataFrom:from toDate:to];
}
Thanks in advance for any feedback!
EDIT
It seems the appropriate method to use for live updates is the following..
- (void)liveSteps {
[self.pedometer startPedometerUpdatesFromDate:[NSDate date]
withHandler:^(CMPedometerData *pedometerData, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Steps %#",pedometerData.numberOfSteps);
});
}];
}
However, even this is severely delayed. Does anyone have any idea how to use this properly to essentially update as the user takes a step?
I can only confirm your findings. I also wanted to get "true" realtime information. As it seems at this point, the API is not capable of this; even by forcing the updates into a queue, sync, async, etc.
For references and others with this question, here is the code I use based on Swift 3 and Xcode 8.2. I simply apply this portion of code in the concerned viewcontroller, after checking the CMPedometer.isStepCountingAvailable().
As you can see, I've included a small animation to update the UILabel in a more fluid manner.
// Steps update in near realtime - UILabel
self.pedoMeter.startUpdates(from: midnightOfToday) { (data: CMPedometerData?, error) -> Void in
DispatchQueue.main.async(execute: { () -> Void in
if(error == nil){
self.todaySteps.text = "\(data!.numberOfSteps)"
// Animate the changes of numbers in the UILabel
UILabel.transition(with: self.todaySteps,
duration: 0.50,
options: .transitionCrossDissolve,
animations: nil,
completion: nil)
}
})
}
I am attempting to create a local calendar on iOS. I request access to EKEntityTypeEvent and have it granted, create the calendar from the EKEventStore (yes, the same instance I requested access from), find the EKSourceTypeLocal then set it on my new calendar. Calling saveCalendar:commit:error (with commit:YES) returns YES and there is no NSError. The resulting calendar has a calendarIdentifier assigned.
But then when I flip to the iOS Calendar app, my calendar is not there! I've tried on the iOS 7 and 8 simulators (after a "Reset content and settings...", so there's no iCloud configured) and on an iCloud-connected iPhone 5s with iOS 8. Nothing works!
What have I missed?
I have created a bare project with the following view controller to illustrate the problem:
#import EventKit;
#import "ViewController.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UILabel *resultLabel;
#property (nonatomic, assign) NSUInteger calendarCount;
#property (nonatomic, strong) EKEventStore *eventStore;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.resultLabel.text = #"";
self.eventStore = [[EKEventStore alloc] init];
}
- (IBAction)userDidTapCreateCalendar:(id)sender {
EKAuthorizationStatus status = [EKEventStore authorizationStatusForEntityType:EKEntityTypeEvent];
if (status == EKAuthorizationStatusNotDetermined) {
__weak typeof(self) weakSelf = self;
[self.eventStore requestAccessToEntityType:EKEntityTypeEvent
completion:^(BOOL granted, NSError *error) {
if (granted) {
[weakSelf createCalendar];
} else {
weakSelf.resultLabel.text = #"If you don't grant me access, I've got no hope!";
}
}];
} else if (status == EKAuthorizationStatusAuthorized) {
[self createCalendar];
} else {
self.resultLabel.text = #"Access denied previously, go fix it in Settings.";
}
}
- (void)createCalendar
{
EKCalendar *calendar = [EKCalendar calendarForEntityType:EKEntityMaskEvent eventStore:self.eventStore];
calendar.title = [NSString stringWithFormat:#"Calendar %0lu", (unsigned long)++self.calendarCount];
[self.eventStore.sources enumerateObjectsUsingBlock:^(EKSource *source, NSUInteger idx, BOOL *stop) {
if (source.sourceType == EKSourceTypeLocal) {
calendar.source = source;
*stop = YES;
}
}];
NSError *error = nil;
BOOL success = [self.eventStore saveCalendar:calendar commit:YES error:&error];
if (success && error == nil) {
self.resultLabel.text = [NSString stringWithFormat:#"Created \"Calendar %0lu\" with id %#",
(unsigned long)self.calendarCount, calendar.calendarIdentifier];
} else {
self.resultLabel.text = [NSString stringWithFormat:#"Error: %#", error];
}
}
#end
I've tested this code on a device, and it works OK (change EKEntityMaskEvent -> EKEntityTypeEvent). I can see new calendar in iOS calendars.
Looks like simulator does not actually saves your calendars. I've got the same issue some time ago.
New to the site and Obj C. Attempting to get a pitch value from Device Motion (working), put into an array with the most recent 60 values (not working) and select the maximum value within the array. With each new pitch value from the device, new pitch value is added to the array and the 61st value is dropped. When I hook up my phone and run, I get the log values for the pitch and maxPitch; however, I am not getting the array of 60 values so I don't believe it is working properly. Any help is greatly appreciated.
I believe the problem may be in the line : if (pitchArray.count <= 60) {
[pitchArray addObject:[NSString stringWithFormat:#"%.2gº", motion.attitude.pitch * kRadToDeg]];
Here is the full code:
#import "ViewController.h"
#import <CoreMotion/CoreMotion.h>
#define kRadToDeg 57.2957795
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UILabel *pitchLabel;
#property (nonatomic, strong) CMMotionManager *motionManager;
#end
#implementation ViewController
- (CMMotionManager *)motionManager
{
if (!_motionManager) {
_motionManager = [CMMotionManager new];
[_motionManager setDeviceMotionUpdateInterval:1/60];
}
return _motionManager;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue] withHandler:^(CMDeviceMotion *motion, NSError *error) {
self.pitchLabel.text = [NSString stringWithFormat:#"%.2gº", motion.attitude.pitch * kRadToDeg];
NSMutableArray *pitchArray = [NSMutableArray array];
pitchArray = [[NSMutableArray alloc] initWithCapacity:60];
if (pitchArray.count <= 60) {
[pitchArray addObject:[NSString stringWithFormat:#"%.2gº", motion.attitude.pitch * kRadToDeg]];
}
else {
[pitchArray removeObjectAtIndex:0];
}
NSNumber *maxPitch = [pitchArray valueForKeyPath:#"#max.intValue"];
NSLog(#"%#",pitchArray);
NSLog(#"Max Pitch Value = %d",[maxPitch intValue]);
}];
}
#end
You keep allocating a new array every time you get a new pitch value. So you shall define the pitch array as a property and allocate it before your motion update handler. Your code would be:
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UILabel *pitchLabel;
#property (nonatomic, strong) CMMotionManager *motionManager;
#property (nonatomic, strong) NSMutableArray *pitchArray;
#end
#implementation ViewController
- (CMMotionManager *)motionManager
{
if (!_motionManager) {
_motionManager = [CMMotionManager new];
[_motionManager setDeviceMotionUpdateInterval:1/60];
}
return _motionManager;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.pitchArray = [[NSMutableArray alloc] initWithCapacity:60];
[self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue] withHandler:^(CMDeviceMotion *motion, NSError *error) {
self.pitchLabel.text = [NSString stringWithFormat:#"%.2gº", motion.attitude.pitch * kRadToDeg];
if (self.pitchArray.count <= 60) {
[self.pitchArray addObject:[NSString stringWithFormat:#"%.2gº", motion.attitude.pitch * kRadToDeg]];
}
else {
[self.pitchArray removeObjectAtIndex:0];
}
NSNumber *maxPitch = [self.pitchArray valueForKeyPath:#"#max.intValue"];
NSLog(#"%#",self.pitchArray);
NSLog(#"Max Pitch Value = %d",[maxPitch intValue]);
}];
}
Ah, simple error. It wasn't looping so I changed the if/else statement to while. The code works now and outputs the 60 item array and max value.
I'm trying to explore CoreMotion and I'm trying to use CMStepCounter class to get the Step counts. here is how I implemented my view controller to get stepCounts
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UILabel *stepsCountingLabel;
#property (nonatomic, strong) CMStepCounter *cmStepCounter;
#property (nonatomic, strong) NSOperationQueue *operationQueue;
#end
#implementation ViewController
- (NSOperationQueue *)operationQueue
{
if (_operationQueue == nil)
{
_operationQueue = [NSOperationQueue new];
}
return _operationQueue;
}
- (void)viewDidLoad
{
[super viewDidLoad];
if ([CMStepCounter isStepCountingAvailable])
{
self.cmStepCounter = [[CMStepCounter alloc] init];
[self.cmStepCounter startStepCountingUpdatesToQueue:self.operationQueue updateOn:1 withHandler:^(NSInteger numberOfSteps, NSDate *timestamp, NSError *error)
{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self updateStepCounterLabelWithStepCounter:numberOfSteps];
}];
}];
}
else
{
self.stepsCountingLabel.text = #"NO STEP COUNTING AVAILABLE";
}
}
- (void)updateStepCounterLabelWithStepCounter:(NSInteger)countedSteps
{
self.stepsCountingLabel.text = [NSString stringWithFormat:#"%ld", (long)countedSteps];
}
But when I walk carrying device, I'm not getting any update but when I shake device It gives me some random numberOfSteps count. Please do let me know if I'm missing out something.
Try below method. I think it will work .Call startStepCalculations method from viewdidload..I am calculating steps for 1 day.
- (void)startStepCalculations {
if (!self.stepCounter) {
self.stepCounter = [[CMStepCounter alloc] init];
}
if (![CMStepCounter isStepCountingAvailable]) {
return;
}
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *components = [cal components:(NSHourCalendarUnit |
NSMinuteCalendarUnit |
NSSecondCalendarUnit)
fromDate:[[NSDate alloc] init]];
[components setHour:-[components hour]];
[components setMinute:-[components minute]];
[components setSecond:-[components second]];
NSDate *start = [cal dateByAddingComponents:components toDate:[[NSDate alloc] init] options:0];
[components setHour:+24];
[components setMinute:0];
[components setSecond:0];
NSDate *end = [cal dateByAddingComponents:components toDate:start options:0];
NSOperationQueue *queue = [NSOperationQueue mainQueue];
[self.stepCounter queryStepCountStartingFrom:start
to:end
toQueue:queue
withHandler:^(NSInteger numberOfSteps, NSError *error) {
if (error) {
NSLog(#"ERROR : %#",error.localizedDescription);
return;
}
self.steps = numberOfSteps;
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:self.steps];
[self beginMonitoringForNewSteps];
}];
}
- (void)beginMonitoringForNewSteps {
if (!self.stepCounter) {
self.stepCounter = [[CMStepCounter alloc] init];
}
NSOperationQueue *queue = [NSOperationQueue mainQueue];
[self.stepCounter startStepCountingUpdatesToQueue:queue
updateOn:1
withHandler:^(NSInteger numberOfSteps, NSDate *timestamp, NSError *error) {
// calculate the total steps (today + new)
self.steps += (int)numberOfSteps;
//TODO badge number should be 1000 max
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:self.steps];
}];
}
Let me know it is working or not.
Happy Coding!!!