I’d been searching on the forum for a touch event handler that would allow me Long Presses, Double Taps, etc, but the solutions I’d found weren’t exactly what I was looking for, so I wrote my own based on some of them.
The ones I’d found were centered around the touched() method, so they only fired when something was touched. seems logical to do it that way, but I wanted my class to not be global, so I figured out a way to have it live exclusively in the draw() function.
The general premise is that it sends out a “NO_TOUCH_EVENT” message every frame, and when there’s a touch message, it figures out what kind to send. Possible types are:
NO_TOUCH_EVENT
BEGAN_EVENT
MOVING_EVENT
ENDED_EVENT
LONG_PRESS_EVENT
DOUBLE_TAP_EVENT
DOUBLE_TAP_AND_DRAG_EVENT
To use it, instead of sending the touch(t) to all of your classes so they can handle the touch, just pass them the singleton’s return value from its draw() and use CurrentTouch to get your x/y:
TouchEvent = class()
TouchEvent.NO_TOUCH_EVENT = 1
TouchEvent.BEGAN_EVENT = 2
TouchEvent.MOVING_EVENT = 3
TouchEvent.ENDED_EVENT = 4
TouchEvent.LONG_PRESS_EVENT = 5
TouchEvent.DOUBLE_TAP_EVENT = 6
TouchEvent.DOUBLE_TAP_AND_DRAG_EVENT = 7
function TouchEvent:init()
self.x = 0 -- x of currentTouch.x when a BEGAN event occurs
self.y = 0 -- y of CurrentTouch.y when a BEGAN event occurs
self.t = 0 -- ElapsedTime for CurrentTouch when a Began event occurs
self.LPStarted = false
self.LPDetected = false
self.screenTouched = false
self.LPThreshold = 0.5
self.wiggleRoom = 10
end
function TouchEvent:draw()
-- Codea does not automatically call this method
if( self.screenTouched == false ) then
return TouchEvent.NO_TOUCH_EVENT
end
--we have a TouchEvent!
-- was LP started?
-- was LP Threshold passed?
-- was Touch XY within limits?
if( self.LPStarted ) then --yes, LP Started
if( self:wasLPThresholdPassed() ) then -- yes, LP threshold passed
if( self:wasTouchXYWithinWiggleRoom() ) then -- yes, Touch XY within limits
return TouchEvent.LONG_PRESS_EVENT
end
end
end
--LP not started
return self:processRegularTouchEvent()
end
function TouchEvent:touched(touch)
-- Codea does not automatically call this method
self.screenTouched = true
end
function TouchEvent:wasTouchXYWithinWiggleRoom()
local result = false
local dx = math.abs(self.x-CurrentTouch.x)
local dy = math.abs(self.y-CurrentTouch.y)
if dx <= self.LPThreshold then
if dy <= self.LPThreshold then
result = true
end
end
return result
end
function TouchEvent:processRegularTouchEvent()
if( CurrentTouch.state ~= BEGAN ) then
self.LPStarted = false
self.LPDetected = false
self.t = 0
if( CurrentTouch.state ~= MOVING ) then
--touchEnded
self.screenTouched = false
return TouchEvent.ENDED_EVENT
else
--it wasn't TouchBegan, or TouchEnded, so it's TouchMoving
if( CurrentTouch.tapCount > 1 ) then
return TouchEvent.DOUBLE_TAP_AND_DRAG_EVENT
end
return TouchEvent.MOVING_EVENT
end
else -- wasTouchBegan = true
if( self.LPStarted == false ) then
--only update this stuff if a LongPress hasn't begun.
--otherwise it would be updated every frame, and we'd never get a LP event, because of ElapsedTime reflecting the time change of every frame, not from when the BEGAN event started
self.x = CurrentTouch.x
self.y = CurrentTouch.y
self.t = ElapsedTime
end
self.LPStarted = true
--check for DoubleTap
if( CurrentTouch.tapCount > 1 ) then
return TouchEvent.DOUBLE_TAP_EVENT
end
return TouchEvent.BEGAN_EVENT
end
end
function TouchEvent:wasLPThresholdPassed()
return (ElapsedTime - self.t > self.LPThreshold )
end
function TouchEvent:turnOffLongPress()
self.LPStarted = false
self.LPDetected = false
end
and here’s the main i used to test it out:
function setup()
te = TouchEvent()
touchTypes = { "NO_TOUCH_EVENT", "BEGAN_EVENT", "MOVING_EVENT", "ENDED_EVENT", "LONG_PRESS_EVENT", "DOUBLE_TAP_EVENT", "DOUBLE_TAP_AND_DRAG_EVENT"}
end
-- This function gets called once every frame
function draw()
-- This sets a dark background color
background(40, 40, 50)
print( touchTypes[ te:draw() ] )
end
function touched(t)
te:touched(t) -- this is the only thing that handles touches
-- everything else will receive te:draw()'s return value and use that to process touches from within the main draw() function
end