Search code examples
iosobjective-cxcodebenchmarking

Is it possible to check that a condition holds true continuously for a certain duration before executing a set of code? Objective C


For example, let's say that some variable changes based on user input. When this variable reaches some condition, it must hold that condition for some set time before some other code is executed.

if(changingVariable == someValue for some amount of time){
    //code to be executed
}

NOTE: I want the answer to be as general as possible so that it can be applied to any situation.

For some context, here is the situation that I will be using it in:

#import "ViewController.h"

@interface ViewController ()
@end

@implementation ViewController
- (CMMotionManager *)motionManager {
    _motionManager = [[CMMotionManager alloc] init];
    _motionManager.accelerometerUpdateInterval = 0.1;
    return _motionManager;
}

- (IBAction)takePhoto:(UIButton *)sender {
    [self isStable];
}

- (void)isStable {
    if(!self.motionManager.isAccelerometerActive) {
        [self.motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAccelerometerData *accData, NSError *error){
                CGFloat x = accData.acceleration.x;
                CGFloat y = accData.acceleration.y;
                //CGFloat z = accData.acceleration.z;           
        }];

        // This is the condition that needs to hold true 
        // for half of a second before executing some code 
        if(fabs(x) < .095 && fabs(y) < .095){
            // Code that takes a picture
        }
    }
}
@end

Edit: I just want to say thanks to all who helped!


Solution

  • Being unaware of your real intention, I'm proposing a bridged solutions covering up the scenarios.

    First declare x and y as class properties

    @property(nonatomic) CGFloat x;
    @property(nonatomic) CGFloat y;
    @property(nonatomic) BOOL scheduled;
    ...
    

    Next modify your motion manager update code like this

    [motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAccelerometerData *accData, NSError *error){
        self.x = accData.acceleration.x;
        self.y = accData.acceleration.y;
    
        // This is the condition that needs to hold true
        // for half of a second before executing some code
        if(fabs(self.x) < .095 && fabs(self.y) < .095) {
            //schedule your takePicture routine to be executed after 0.5 seconds
            //if not already scheduled
            if (!self.scheduled) {
                [self performSelector:@selector(takePicture) withObject:nil afterDelay:0.5];
                self.scheduled = YES;
            }
        }
        /*
        else {
            //cancel previously scheduled method 
            //if the condition doesn't hold true in an intermediate update
            [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(takePicture) object:nil];
    
            //reset the flag so that further scheduling is possible
            self.scheduled = NO;
        }
        */
    }];
    

    And now in your takePicture routine reevaluate the condition and execute code only if the condition is true:

    -(void)takePicture {
        if (fabs(self.x) < .095 && fabs(self.y) < .095) {
            // Code that takes a picture
        }
    
        //reset the flag so that further scheduling is possible
        self.scheduled = NO;
    }
    

    So to summarize things:

    1. We capture accelerometer updates for each frame.
    2. Schedule a takePicture routine after 0.5 seconds
    3. In the takePicture routine reevaluate the condition and if it still holds true we execute capture code.

    So what's left? Here we have considered only the terminal conditions -- we evaluated the condition just before 0.5 seconds interval commences and just after it ends. But it doesn't guarantee the condition holds true in between. If we also need to conform to this scenario, we need to cancel the previously scheduled routine if the condition deviates from its true state and that can be achieved by uncommenting the else block in motion manager update portion that I've commented out for the time being.