Measuring the time taken when shaking device - iphone

I am developing an application that measures the total time of a shake event. In other words, the timer starts when the shake event is started, and the timer stops when the shake event ends.
So my question is, if we hold two iTouches in a single hand and then shake both at the same time, will the shake event generate the same value on both iTouches?
This is my code:
-(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (motion == UIEventSubtypeMotionShake) {
timer = [NSTimer scheduledTimerWithTimeInterval:0.00025 target:self selector:#selector(showactivity) userInfo:nil repeats:YES];
}
}
-(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (motion == UIEventSubtypeMotionShake) {
[timer invalidate];
}
}

I'd just make an instance variable of type NSDate called shakesBegan, or whatever. Then just call this in motionBegan:
-(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (motion == UIEventSubtypeMotionShake) {
shakesBegan = [NSDate date];
}
}
-(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (motion == UIEventSubtypeMotionShake) {
NSDate *shakesFinished = [NSDate date];
NSTimeInterval shakeTime = [shakesFinished timeIntervalSinceDate:shakesBegan];
NSLog(#"Shaken for %f seconds",shakeTime);
}
}
The output should say something like Shaken for 1.233242 seconds. However, I don't have a device to test this so your mileage may vary.
The reason you're getting an error is because you must make shakesBegan an instance variable. Like this in your interface:
#interface Something : UIViewController
{
NSDate *shakesBegan;
}
#end

Related

Call a function if userinteraction is nil for 3 seconds

Guys i need to call a function in such a view that if there is no user interaction for three seconds the function should be called
what should i do?
Can any one pls give a logic or link which help this?
Thanks
My recommendation is to override - (void)touchesBegan: withEvent: on that view.
When you receive a touch event, set a timer, and make its time 3 seconds. If you receive a touch and there is a timer, change its fire date to be 3 seconds after the touch time. When the timer fires, call the function, and set the timer to nil.
You can play with the movement, removals and cancellation methods to make your timer fire correctly and at the right time for the activity required.
Use NSTimer as:-
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView:self.view];
if ([touch view] == self.view)
{
NSTimer *updateTimer = [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:#selector(updateTime) userInfo:nil repeats:YES];
}
}
-(void)updateTime
{
NSLog(#"....Update Function Called....");
[self functionName];
}
Use this for calling function.Check it Hope it helps.Thanks :)

iOS Loses Touches?

I can't find anything to explain lost UITouch events. If you smash your full hand on the screen enough times, the number of touchesBegan will be different than the number of touchesEnded! I think the only way to actually know about these orphaned touches will be to reference them myself and keep track of how long they haven't moved.
Sample code:
int touchesStarted = 0;
int touchesFinished = 0;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
touchesStarted += touches.count;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
touchesFinished += touches.count;
NSLog(#"%d / %d", touchesStarted, touchesFinished);
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesEnded:touches withEvent:event];
}
Don't forget about touchesCancelled: UIResponder reference
Editing in response to the poster's update:
Each touch object provides what phase it is in:
typedef enum {
UITouchPhaseBegan,
UITouchPhaseMoved,
UITouchPhaseStationary,
UITouchPhaseEnded,
UITouchPhaseCancelled,
} UITouchPhase;
I believe that if a touch starts and ends in the same touch event set, -touchesBegan:withEvent: will be called but will contain touches which have ended or cancelled.
You should change your counting code, then, to look like this:
int touchesStarted = 0;
int touchesFinished = 0;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self customTouchHandler:touches];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self customTouchHandler:touches];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self customTouchHandler:touches];
}
- (void)customTouchHandler:(NSSet *)touches
{
for(UITouch* touch in touches){
if(touch.phase == UITouchPhaseBegan)
touchesStarted++;
if(touch.phase == UITouchPhaseEnded || touch.phase == UITouchPhaseCancelled)
touchesFinished++;
}
NSLog(#"%d / %d", touchesStarted, touchesFinished);
}
Every single touch event will go through both phases of started and finished/cancelled, and so your counts should match up as soon as your fingers are off the screen.
One thing to remember... You may receive some touches that have multiple taps. Don't forget to take tapCount into account.
However, if you still have the problem, you can consider all touches from the event, though it presents some other management issues...
HACK ALERT
I have coded the following HACK to work around this. Sometimes the touchesEnded does not get called, BUT, the touches show up as part of all the touches in the event.
Note, that you can now process the same "canceled" or "ended" touch multiple times. If that is a problem, you have to keep your own state of "pending" touches, and remove them when done.
Yeah, it all is pretty bad, but I don't know how to overcome this issue without a similar hack. The basic solution is to look at all the touches in each event, and process them based on their phase, calling the appropriate ended/canceled when they are seen.
- (void) touchesEndedOrCancelled:(NSSet *)touches
{
__block NSMutableSet *ended = nil;
__block NSMutableSet *canceled = nil;
[touches enumerateObjectsUsingBlock:^(UITouch *touch, BOOL *stop) {
if (touch.phase == UITouchPhaseEnded) {
if (!ended) ended = [NSSet setWithObject:touch];
else [ended addObject:touch];
} else if (touch.phase == UITouchPhaseCancelled) {
if (!canceled) canceled = [NSSet setWithObject:touch];
else [canceled addObject:touch];
}
}];
if (ended) [self touchesEnded:ended withEvent:nil];
if (canceled) [self touchesCancelled:canceled withEvent:nil];
}
Then, call it at the end of touchesBegan and touchesMoved...
[self touchesEndedOrCancelled:event.allTouches];
For this to work, touchesEnded/Canceled needs to not choke on a nil event. Also, the "other" needs to be handled. In touchesEnded...
[self touchesCancelled:[event.allTouches objectsPassingTest:^BOOL(UITouch *touch, BOOL *stop) {
return touch.phase == UITouchPhaseCancelled;
}] withEvent:nil];
and in touchesCanceled...
[self touchesEnded:[event.allTouches objectsPassingTest:^BOOL(UITouch *touch, BOOL *stop) {
return touch.phase == UITouchPhaseEnded;
}] withEvent:nil];
You may need to provide more detail but....
If you run your finger off the edge of the screen this event will show touch started and touch moved but no end as it didn't actually end (lift finger). This could be your problem, that the event didn't happen. Plus there is a limit to the amount of touches in multi touch if you press say 10 times its then up to the system to determine what events are really and what are false, it seems like you are using the hardware incorrectly.

Delay Touch Response

I have a variable called touchStatus that tracks the touch status in the program. The variable gets set to B in the touchesBegan method, E in touchesEnded and to M in touchesMoved.
However, my requirements are a little different. I was requested to program in a way so that there is one second of delay between the finger being lift off from the screen and touchStatus getting set to E. If the user touches the screen before the one second elapses, touchStatus should continue to be M or B (whatever it was before the one second).
How can I accomplish this?
You can use
[self performSelector:#selector(setEndedValue:) withObject:self afterDelay:1.0];
Create a BOOL to monitor whether the value should be set such as:
BOOL hasTouchRestarted = NO;
If the screen is touched again before the value is set, change the value to YES and return from the setEndedValue method.
-(void)setEndedValue {
if ( hasTouchRestarted ) { return; }
// set value
self.touchStatus = E;
}
In the touchEnded routine set up an NSTimer task to invoke a selector in one second. If there is another touch before then, cancel the timer task.
Use a NSTimer *timer ivar to initiate a delayed call, and cancel the call if the user lifts the finger before one second.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
self.myvar = #"B";
[self.timer invalidate];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(handleOneSecondPress) userInfo:nil repeats:NO];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent*)event {
[self.timer invalidate];
self.timer = nil;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
self.myvar = #"M";
}
- (void)handleOneSecondPress {
self.timer = nil;
self.myvar = #"E";
}

touchBegan timestamp

In my game I need to calculate duration of touch. I did this by :
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
self.endTime = [NSDate date]; //NSDate *endTime in .h
NSLog(#"%#",self.endTime);
}
-(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
tStart = [[NSDate date] timeIntervalSinceDate:self.endTime];
NSLog(#"duration %f",tStart);
I am using this time interval as a factor to calculate height of the jump the player makes.
Less is the tStart, low is the jump and more is the tStart, high is the jump. I am doing this as :
if(tStart/1000<=9.430)
{
[player jump:5.0f];
}
else if(tStart>9.430 && tStart<=9.470)
{
[player jump:7.0f];
}
else if(tStart/1000>9.470)
{
[player jump:8.0f];
}
However I want to perform this action on tochBegan so that player may jump as soon as screen is touched. For this need the value of tStart in touchBegan. How should I do that?
Thanks
For a given touch, the UITouch instance is the same, so at ccTouchBegan save the touch/touches with the oldest timestamp and then wait for ccTouchEnded. When you get the UITouch that you previously saved, it means the player lifted the finger.
update
You can
Jump as soon as the user touches the screen.
Jump and boost the jump while the touch is ongoing up to x milliseconds (suggestion from grapefrukt).
Jump when the user releases the touch or a max time of x milliseconds has elapsed.
Jump and boost the jump until the touch is over.
Option 4 is unpractical because the user can keep pressing as long as he wants. So given that you want to make a variable jump, here is code for option 3:
UITouch *jump = nil;
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
// no jump ongoing, start one
if (jump==nil){
jump = touch;
// jump on a separate thread
[NSThread performSelectorInBackground:(SEL)jump withObject:touch];
}
}
-(void) jump:(UITouch*) touch {
NSInterval time = [touch timestamp]; // I *think* this is the time since system uptime :P
int jumpSpeed = 1;
// increase the jump speed for every 100ms up to 'maxTimeYouAreAllowedToJump' ms
while ( ([NSProcessInfo systemUptime]-time < maxTimeYouAreAllowedToJump) && (jump!=nil) ){
jumpSpeed += 1;
[NSThread sleepForTimeInterval:.1]; // sleep for 100ms
}
[self doTheJumpingWithSpeed:jumpSpeed]; // do the actual jump!
}
-(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event {
// user released the touch that initiated the jump
if ((touch!=nil) && (touch==jump)) {
jump =nil;
}
}

Anyone have code to substitute for UILongPressGestureRecognizer?

I'd like to support pre 3.2 and this is the only symbol that doesn't want to cooperate, anyone know of some touchesmoved code or something i can use in lieu of the UILongPressGestureRecognizer?
Thanks,
Nick
As you know, you should use touchesBegan, Moved, Ended, and Canceled functions for pre 3.2 iOS.
I think implementing only touchesMoved is bad because if the user presses and doesn't move at all until releasing, then touchesMoved won't get called ever.
Instead, I used NSTimer to acheive a long press touch event.
This might not be an optimal solution, but it worked well for my app.
Here's a snippet of code.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
isAvailable = NO;
timer = [NSTimer scheduledTimerWithTimeInterval:DURATION target:self selector:#selector(didPassTime:) userInfo:nil repeats:NO];
}
- (void)didPassTime:(id)sender{
isAvailable = YES;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
if(isAvailable == YES){
// still pressing after 0.5 seconds
}
else{
// still pressing before 0.5 seconds
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
if(isAvailable == YES){
// releasing a finger after 0.5 seconds
}
else {
// releasing a finger before 0.5 seconds
[timer invalidate];
timer = nil;
}
}