'Tapping' through a 'draggable' touch event object

Looked through tonnes of articles, but none I found, gave a solution for my dilemma.

I have 2 images, one above the other, both images have their own touch events attached.

Top image can only be ‘dragged’, while the bottom image can be either ‘tapped’ or ‘dragged’.

If the user does not ‘drag’ but instead ‘taps through’ the top image (in the hopes of starting a touch event for the bottom image) how do i get the touch event passed down to the bottom image?

If I put the return true within the top image’s “moved” event.phase and no return true at the end of the touch function then the top button doesn’t receive any touch events as it goes straight to the bottom image.

If I put the return true at the end of the touch function this will allow the top image to be ‘dragged’ but then the bottom image will never be able to receive an event if the user attempts to ‘tap through’ not ‘drag’ the top image. 

Any ideas?

Can you add a “fake” tap function to the top image, which just returns false?  

local function ignoreTap() return false end topImage:addEventListener("tap", ignoreTap)

This should allow the top image to acknowledge a tap event but let it propogate through to the lower object. I’m guessing that in your case, the “began” phase of the top image’s touch function is preventing the tap from getting through.  

Maybe you could even just put return false at the end of the top images touch function, with return true in the “moved” phase?

Thanks for feedback, unfortunately can’t seem to get it to work no matter how i re-arrange things or add in a TAP event to the top or bottom image. I’m not even using a ‘began’ event on the top image as that would already block any ‘began’ event for the bottom image.

Basically adding in a return anywhere inside the event.phase conditions won’t correctly return as the moment you don’t have a return outside in the main event function it will be considered not returning true and propagate through to the bottom image (thereby preventing  ‘dragging’ of the top image)

No worries I guess i will just have to do it completely different and make the top image a TAP to select event image and then tap again somewhere else to move the top image there.

Would be good if the touch event api could be tweaked in such a way as to allow returns to work properly inside the event.phase conditions and not only outside at the end of the main event.

Still interested to see if anyone has figured out how to have 2 touch event images (objects) one on top of the other and get them both to work properly.

maybe add a tap event to the top image and when tapped it moves it behind the other then it will be on top and can be dragged

@jstrahan, might be a good idea :slight_smile:

Although for my purpose won’t work since my bottom image is actual a ground map image, the top image is actually a chopper. But i have made it work another way, still keeping both as touch events, however I made the top image only ‘tappable’ (so no longer draggable) and then requiring another tap on the bottom image to move the top image there (user-wise it still makes sense). Appreciate the feedback though :slight_smile:

My understanding from your initial post is that you want to drag both images, but the bottom image can also receive tap events through the top image. To do just that here is my code:

local a, b = display.newRect(0,0,500,500), display.newRect(100,100,200,200) b:setFillColor(0,255,0) a.name = "bottom" b.name = "top" local function touch(e)     print("touch "..e.target.name.." "..e.phase)     if (e.phase == "began") then         e.target.hasFocus = true         display.getCurrentStage():setFocus(e.target)         e.target.prev = e         return true     elseif (e.target.hasFocus) then         if (e.phase == "moved") then             e.target.x, e.target.y = e.target.x+(e.x-e.target.prev.x), e.target.y+(e.y-e.target.prev.y)             e.target.prev = e         else             e.target.hasFocus = false             display.getCurrentStage():setFocus(nil)             e.target.prev = nil         end         return true     end     return false end local function tap(e)     print("tap "..e.target.name)     return true end a:addEventListener("touch",touch) a:addEventListener("tap",tap) b:addEventListener("touch",touch)

Running the code above you’ll notice that any tap event is also preceded by two touch events (began and ended phases.) This is because a tap event is the accumulation of a start and end of a touch. (To be really good, the tap should allow a little movement - moved phases - between the began and ended phases, because human fingers just aren’t that precise.)

If you want to avoid the began and ended phases of touch events and only receive a single tap event, you’ll need to use only touch events (don’t listen for tap events) and do a lot of work to reduce the two phases down to a single tap. I have tried this before and produced a couple of libraries which might help you, posted to the code exchange - please be aware that this is pretty old code now, but it certainly worked well in 2011:

http://developer.coronalabs.com/code/sanitised-touch-library

You can find my other libraries, samples and tutorials here:

http://springboardpillow.blogspot.co.uk/2012/04/sample-code.html

Hope this helps.

@horacebury, appreciate the great feedback thanks, looks quite similar to the pinch-zoom code i’m using now except that i went and created different touch functions for each object which i guess is not the best way of coding :slight_smile:

There’s absolutely no problem creating separate listener functions for each object. You just need to make sure you return true or false in every eventuality. The code I posted could easily have the touch function copied and doing something different for each object, as long as it returns sensible true|false values at the end of each.

If you want to do something more advanced, get in touch. As mentioned, I have written code which uses only the touch event to block began/ended phases and fire a fake tap event if necessary. The problem with that approach is that you need to then dispatch a tap event using dispatchEvent, which does not percolate down through the display object hierarchy.

Can you add a “fake” tap function to the top image, which just returns false?  

local function ignoreTap() return false end topImage:addEventListener("tap", ignoreTap)

This should allow the top image to acknowledge a tap event but let it propogate through to the lower object. I’m guessing that in your case, the “began” phase of the top image’s touch function is preventing the tap from getting through.  

Maybe you could even just put return false at the end of the top images touch function, with return true in the “moved” phase?

Thanks for feedback, unfortunately can’t seem to get it to work no matter how i re-arrange things or add in a TAP event to the top or bottom image. I’m not even using a ‘began’ event on the top image as that would already block any ‘began’ event for the bottom image.

Basically adding in a return anywhere inside the event.phase conditions won’t correctly return as the moment you don’t have a return outside in the main event function it will be considered not returning true and propagate through to the bottom image (thereby preventing  ‘dragging’ of the top image)

No worries I guess i will just have to do it completely different and make the top image a TAP to select event image and then tap again somewhere else to move the top image there.

Would be good if the touch event api could be tweaked in such a way as to allow returns to work properly inside the event.phase conditions and not only outside at the end of the main event.

Still interested to see if anyone has figured out how to have 2 touch event images (objects) one on top of the other and get them both to work properly.

maybe add a tap event to the top image and when tapped it moves it behind the other then it will be on top and can be dragged

@jstrahan, might be a good idea :slight_smile:

Although for my purpose won’t work since my bottom image is actual a ground map image, the top image is actually a chopper. But i have made it work another way, still keeping both as touch events, however I made the top image only ‘tappable’ (so no longer draggable) and then requiring another tap on the bottom image to move the top image there (user-wise it still makes sense). Appreciate the feedback though :slight_smile:

My understanding from your initial post is that you want to drag both images, but the bottom image can also receive tap events through the top image. To do just that here is my code:

local a, b = display.newRect(0,0,500,500), display.newRect(100,100,200,200) b:setFillColor(0,255,0) a.name = "bottom" b.name = "top" local function touch(e)     print("touch "..e.target.name.." "..e.phase)     if (e.phase == "began") then         e.target.hasFocus = true         display.getCurrentStage():setFocus(e.target)         e.target.prev = e         return true     elseif (e.target.hasFocus) then         if (e.phase == "moved") then             e.target.x, e.target.y = e.target.x+(e.x-e.target.prev.x), e.target.y+(e.y-e.target.prev.y)             e.target.prev = e         else             e.target.hasFocus = false             display.getCurrentStage():setFocus(nil)             e.target.prev = nil         end         return true     end     return false end local function tap(e)     print("tap "..e.target.name)     return true end a:addEventListener("touch",touch) a:addEventListener("tap",tap) b:addEventListener("touch",touch)

Running the code above you’ll notice that any tap event is also preceded by two touch events (began and ended phases.) This is because a tap event is the accumulation of a start and end of a touch. (To be really good, the tap should allow a little movement - moved phases - between the began and ended phases, because human fingers just aren’t that precise.)

If you want to avoid the began and ended phases of touch events and only receive a single tap event, you’ll need to use only touch events (don’t listen for tap events) and do a lot of work to reduce the two phases down to a single tap. I have tried this before and produced a couple of libraries which might help you, posted to the code exchange - please be aware that this is pretty old code now, but it certainly worked well in 2011:

http://developer.coronalabs.com/code/sanitised-touch-library

You can find my other libraries, samples and tutorials here:

http://springboardpillow.blogspot.co.uk/2012/04/sample-code.html

Hope this helps.

@horacebury, appreciate the great feedback thanks, looks quite similar to the pinch-zoom code i’m using now except that i went and created different touch functions for each object which i guess is not the best way of coding :slight_smile:

There’s absolutely no problem creating separate listener functions for each object. You just need to make sure you return true or false in every eventuality. The code I posted could easily have the touch function copied and doing something different for each object, as long as it returns sensible true|false values at the end of each.

If you want to do something more advanced, get in touch. As mentioned, I have written code which uses only the touch event to block began/ended phases and fire a fake tap event if necessary. The problem with that approach is that you need to then dispatch a tap event using dispatchEvent, which does not percolate down through the display object hierarchy.