Distinguishing between a touch and touch and hold?

I need to distinguish between a touch on an object or a touch and hold. For a single touch without moving there would be an action but if the object is touched and held it would be possible to drag the object around.

You could listen for the “began” phase of the touch event and start a timer for say a second, then cancel that timer when the “moved” or “ended” phase is fired.

If a second passes before those phases are fired then your listener function would get called meaning they are holding the object.

Below is some example, untested, code to show what I mean. Naturally you’ll need more than this to actually do the other stuff that you want but hopefully this will help you get the idea.

local object = display.newRect( 0, 0, 50, 50 ) local t local onHold = function() -- the object is now being held end local onTouch = function( event ) if event.phase == "began" then t = timer.performWithDelay( onHold, 1000, 1 ) else timer.cancel( t ) t = nil end end object:addEventListener( "touch", onTouch )

Ok I was thinking about a timer but for some reason thought that it might not be the best way. I did it before in another language/framework without timers you see. So the timer would start but if the user started moving then the timer would cancel straight away.

Not sure what this error is about: “timer.cancel(): invalid timer (expected a table but got a nil)”

EDIT: Looks like it should be local t = {} but strangely doing that throws a different error:

Runtime error ?:0: attempt to perform arithmetic on field ‘_time’ (a nil value)

If the timer hasn’t been created then “t” will be nil, so you may want to put a check around the cancel function like this:

if t then timer.cancel( t ) t = nil end

But this code wasn’t tested and wasn’t really meant to be used as is in yours, more of just a way of showing what I meant.

 local tim = {} if event.phase == "began" then tim = timer.performWithDelay( doThis(), 1000, 1 ) else if tim then timer.cancel( tim ) tim = nil end doThat() end

Still not sure why it’s not working though. It’s just a simple timer. I’m getting the following error when trying to cancel it:

Runtime error ?:0: attempt to perform arithmetic on field '\_time' (a nil value)

In my original code I had the parameter order incorrect, the first parameter should be time, the second should be the variable that holds the callback function, and the third is optional but it’s the number of iterations.

It should be this:

timer.performWithDelay( 1000,  doThis, 1 )

Also it’s important to not include the brackets for the callback function in the creation as if you do you’re not passing in the actual function but rather you can calling the function right then and passing in whatever return value it has, which in this case is nil.

Your timer is then getting created with a “nil” time value as the first parameter and the value 1000 for the callback function.

 local tim = {} if event.phase == "began" then tim = timer.performWithDelay( 1000, doThis, 1 ) else if tim then timer.cancel( tim ) tim = nil end doThat() end

It still has the exact same error like this. No idea, thanks for your help anyway. My background is javascript and PHP…things like cancelling a timer were always extremely simple. Not sure what this is about. Commenting out the timer cancel gets rid of the error of course.

It’s probably not gonna work doing it this way anyway because I need to pass arguments with the function.

You don’t need to predeclare your timer at the top as a table, just declare it as nil

local tim = nil

Or simply:

local tim

If you predeclare it as a table but then it’s never actually created, cancelling it will still fail as all we’re doing is checking if the timer variable isn’t nil and assuming that means it’s a timer, but because you’ve predeclared it as a table it now isn’t nil so we try to cancel it, but it’s not actually a timer so it errors out.

Ok thanks, that fixed it. I changed it to 

local tim = {}

which fixed the original table error but in the end was causing this other error. That along with the arguments being passed with the function which you also pointed out. It no longer throws any errors but I needed to pass the function in the performWithDelay with arguments. Oh well.

You don’t want to declare it as a table, which you have done by including the curly braces.

You can still pass a function with arguments, using either a closure or a parameter table - https://docs.coronalabs.com/api/library/timer/performWithDelay.html#passing-parameters-1

Oh great thanks.

No worries, happy to help!

And there will no doubt be other ways to achieve the result you want, a timer is just one way and I’m sure you could think of others.

This tutorial may be of use:

https://docs.coronalabs.com/tutorial/events/continuousActions/index.html

Rob

You guys sure are helpful, they weren’t kidding when they said the Corona community are so helpful. Although annoyingly I achieved it without using timers in the end.  -_-

You should show your solution so others who follow can see it too.

Ok sure. I added a local variable to store whether the object was in motion or not. So then I just added an ‘if’ at the “ended” part of the event.phase to check whether it was in motion or not. If not then it wasn’t being dragged and thus was just a click. Doing it this way won’t provide super fast responsiveness since it won’t respond until the touch has ended but it was fine for my uses.

You could listen for the “began” phase of the touch event and start a timer for say a second, then cancel that timer when the “moved” or “ended” phase is fired.

If a second passes before those phases are fired then your listener function would get called meaning they are holding the object.

Below is some example, untested, code to show what I mean. Naturally you’ll need more than this to actually do the other stuff that you want but hopefully this will help you get the idea.

local object = display.newRect( 0, 0, 50, 50 ) local t local onHold = function() -- the object is now being held end local onTouch = function( event ) if event.phase == "began" then t = timer.performWithDelay( onHold, 1000, 1 ) else timer.cancel( t ) t = nil end end object:addEventListener( "touch", onTouch )

Ok I was thinking about a timer but for some reason thought that it might not be the best way. I did it before in another language/framework without timers you see. So the timer would start but if the user started moving then the timer would cancel straight away.

Not sure what this error is about: “timer.cancel(): invalid timer (expected a table but got a nil)”

EDIT: Looks like it should be local t = {} but strangely doing that throws a different error:

Runtime error ?:0: attempt to perform arithmetic on field ‘_time’ (a nil value)