Touch event - end phase is lost when touch moved

Hello,

I know I am resuming a trite and already discussed problem, but the solutions I found didn’t work in my case or I didn’t manage to apply them properly. So please help clarify this issue.

It’s about this object that serves as button:

  • If touch begins and ends on it everything is good.

  • If touch starts on it and finger is moved off of it end phase never gets triggered.

  • If touch is moved and lands on another touch object this latter only receives the end phase.

Here’s the code:

local function plusAll(event) &nbsp; if event.phase == "began" then &nbsp;&nbsp;&nbsp; beganTime = event.time &nbsp;&nbsp;&nbsp; plusAllButton.alpha = .5 &nbsp; elseif event.phase == "ended" then &nbsp;&nbsp;&nbsp; endedTime = event.time &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; plusAllButton.alpha=1 &nbsp;&nbsp;&nbsp; if (endedTime - beganTime) \< 800 then -- does stuff &nbsp;&nbsp;&nbsp; else --&nbsp; does other stuff &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; end &nbsp; end &nbsp; return true end

I tried adding and removing focus respectively on begin and end phases but if touch is dragged off button focus gets never released (as no ended phase is dispatched)

I tried adding a moved phase and focus is released but still and end phase is being dispatched to an eventual other button that receives the final touch.

How to solve this thing???

Thanks.

setting/removing focus should work. In your “begin” phase use display.currentStage:setFocus(button) (where button is your button object) and in your “ended” phase use display.currentStage:setFocus(nil).

The “ended” phase will fire even if the touch is moved off the button and released.

Thanks @ingemar!!!

I was using display.GETcurrentstage and I guess this was making the difference… Now it’s greatly improved.

Any possibility of actually ‘interrupting’ the phase cycle (not dispatching an end phase)  if  touch is dragged off the button ?

display.getCurrentStage() should also work.
 
To detect if a touch is released on the button you could implement a function to check if the touch is currently on it.

local function eventWithinBounds(obj, event) local bounds = obj.contentBounds local x, y = event.x, event.y if ((x \>= bounds.xMin) and (x \<= bounds.xMax) and (y \>= bounds.yMin) and (y \<= bounds.yMax)) then return true end return false end local stage = display.getCurrentStage() local function onButtonTouch(event) local phase = event.phase local button = event.target if phase == "began" then stage:setFocus(button) button.isButtonPressed = true elseif phase == "moved" then if eventWithinBounds(button, event) then button.isButtonPressed = true &nbsp; else button.isButtonPressed = false end elseif phase == "ended" then stage:setFocus(nil) if button.isButtonPressed then button.isButtonPressed = false -- do something end end return true end

Hello @ingemar,

thanks for the answer. I’ve been experimenting with the following function so far without regards to the button boundaries, in the sense that I just release focus at the first move of the touch. The focus gets released from the object it started on but the touch still gets transfered to another button if I don’t lift the finger and move it onto this other object. Why do you think this is still happening?

local function plusAll(event) &nbsp; if event.phase == "began" then &nbsp;&nbsp;&nbsp; beganTime = event.time &nbsp;&nbsp;&nbsp; plusAllButton.alpha = 0.5 &nbsp; display.currentStage:setFocus(plusAllButton) &nbsp; elseif event.phase == "moved" then &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; display.currentStage:setFocus(nil) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; plusAllButton.alpha = 1 return true &nbsp; elseif event.phase == "ended" then &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; endedTime = event.time &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; plusAllButton.alpha=1 &nbsp;&nbsp;&nbsp; display.currentStage:setFocus(nil) &nbsp;&nbsp;&nbsp; if (endedTime - beganTime) \< 800 then -- does stuff on other button if touch is transferred ???? &nbsp;&nbsp;&nbsp; else -- does stuff on other button if touch is transferred ???? &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; end &nbsp; end &nbsp; return true end

For proper button handling you need to implement a bounds check and only release button focus on an “ended” event to avoid unexpected behaviour.

Hi @ingemar,

thank you so much!!! Now I understand it better and implemented your code and it works great!!!

the only thing is I tested the ‘combo’ function system on only one button so I passed to eventWithinBounds only the event parameter and then used event.target instead of obj.

Obviously I’d need to use the same function for all my buttons, but how I do I pass the ‘obj’ paramater and ‘event’ when obj would correspond to a different button each time? I know I can’t do that from the eventListener I attach to every singlebutton or can I ?

basically I am stuck on:

buttonX:addEventListener(“touch”, onButtonTouch ) – ok to handle the touch effect

buttonX:addEventListener(“touch”, eventWithinBounds(obj, event)) — which I guess can’t be done…

thanks for the great help you’re helping me to learn a lot!!!

I guess the easiest is to add an ID to each button so that you can tell which button has been pressed in the event handler.
(The ID can be anything you want)

Something like:

buttonX.id = "play" buttonX:addEventListener("touch", onButtonTouch ) buttonY.id = "options" buttonY:addEventListener("touch", onButtonTouch ) buttonZ.id = "rate" buttonZ:addEventListener("touch", onButtonTouch )

 

I’ve modified my event handler from above to take the ID into consideration:

local function eventWithinBounds(obj, event) local bounds = obj.contentBounds local x, y = event.x, event.y if ((x \>= bounds.xMin) and (x \<= bounds.xMax) and (y \>= bounds.yMin) and (y \<= bounds.yMax)) then return true end return false end local stage = display.getCurrentStage() local function onButtonTouch(event) local phase = event.phase local button = event.target if phase == "began" then stage:setFocus(button) button.isButtonPressed = true elseif phase == "moved" then if eventWithinBounds(button, event) then button.isButtonPressed = true else button.isButtonPressed = false end elseif phase == "ended" then stage:setFocus(nil) if button.isButtonPressed then button.isButtonPressed = false if button.id == "play" then -- play elseif button.id == "options" then -- show options elseif button.id == "rate" then -- show rate app end end end return true end

setting/removing focus should work. In your “begin” phase use display.currentStage:setFocus(button) (where button is your button object) and in your “ended” phase use display.currentStage:setFocus(nil).

The “ended” phase will fire even if the touch is moved off the button and released.

Thanks @ingemar!!!

I was using display.GETcurrentstage and I guess this was making the difference… Now it’s greatly improved.

Any possibility of actually ‘interrupting’ the phase cycle (not dispatching an end phase)  if  touch is dragged off the button ?

display.getCurrentStage() should also work.
 
To detect if a touch is released on the button you could implement a function to check if the touch is currently on it.

local function eventWithinBounds(obj, event) local bounds = obj.contentBounds local x, y = event.x, event.y if ((x \>= bounds.xMin) and (x \<= bounds.xMax) and (y \>= bounds.yMin) and (y \<= bounds.yMax)) then return true end return false end local stage = display.getCurrentStage() local function onButtonTouch(event) local phase = event.phase local button = event.target if phase == "began" then stage:setFocus(button) button.isButtonPressed = true elseif phase == "moved" then if eventWithinBounds(button, event) then button.isButtonPressed = true &nbsp; else button.isButtonPressed = false end elseif phase == "ended" then stage:setFocus(nil) if button.isButtonPressed then button.isButtonPressed = false -- do something end end return true end

Hello @ingemar,

thanks for the answer. I’ve been experimenting with the following function so far without regards to the button boundaries, in the sense that I just release focus at the first move of the touch. The focus gets released from the object it started on but the touch still gets transfered to another button if I don’t lift the finger and move it onto this other object. Why do you think this is still happening?

local function plusAll(event) &nbsp; if event.phase == "began" then &nbsp;&nbsp;&nbsp; beganTime = event.time &nbsp;&nbsp;&nbsp; plusAllButton.alpha = 0.5 &nbsp; display.currentStage:setFocus(plusAllButton) &nbsp; elseif event.phase == "moved" then &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; display.currentStage:setFocus(nil) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; plusAllButton.alpha = 1 return true &nbsp; elseif event.phase == "ended" then &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; endedTime = event.time &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; plusAllButton.alpha=1 &nbsp;&nbsp;&nbsp; display.currentStage:setFocus(nil) &nbsp;&nbsp;&nbsp; if (endedTime - beganTime) \< 800 then -- does stuff on other button if touch is transferred ???? &nbsp;&nbsp;&nbsp; else -- does stuff on other button if touch is transferred ???? &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; end &nbsp; end &nbsp; return true end

For proper button handling you need to implement a bounds check and only release button focus on an “ended” event to avoid unexpected behaviour.

Hi @ingemar,

thank you so much!!! Now I understand it better and implemented your code and it works great!!!

the only thing is I tested the ‘combo’ function system on only one button so I passed to eventWithinBounds only the event parameter and then used event.target instead of obj.

Obviously I’d need to use the same function for all my buttons, but how I do I pass the ‘obj’ paramater and ‘event’ when obj would correspond to a different button each time? I know I can’t do that from the eventListener I attach to every singlebutton or can I ?

basically I am stuck on:

buttonX:addEventListener(“touch”, onButtonTouch ) – ok to handle the touch effect

buttonX:addEventListener(“touch”, eventWithinBounds(obj, event)) — which I guess can’t be done…

thanks for the great help you’re helping me to learn a lot!!!

I guess the easiest is to add an ID to each button so that you can tell which button has been pressed in the event handler.
(The ID can be anything you want)

Something like:

buttonX.id = "play" buttonX:addEventListener("touch", onButtonTouch ) buttonY.id = "options" buttonY:addEventListener("touch", onButtonTouch ) buttonZ.id = "rate" buttonZ:addEventListener("touch", onButtonTouch )

 

I’ve modified my event handler from above to take the ID into consideration:

local function eventWithinBounds(obj, event) local bounds = obj.contentBounds local x, y = event.x, event.y if ((x \>= bounds.xMin) and (x \<= bounds.xMax) and (y \>= bounds.yMin) and (y \<= bounds.yMax)) then return true end return false end local stage = display.getCurrentStage() local function onButtonTouch(event) local phase = event.phase local button = event.target if phase == "began" then stage:setFocus(button) button.isButtonPressed = true elseif phase == "moved" then if eventWithinBounds(button, event) then button.isButtonPressed = true else button.isButtonPressed = false end elseif phase == "ended" then stage:setFocus(nil) if button.isButtonPressed then button.isButtonPressed = false if button.id == "play" then -- play elseif button.id == "options" then -- show options elseif button.id == "rate" then -- show rate app end end end return true end