Tap/Touch Anatomy

Most Corona developers understand the concepts behind tap and touch events, but the lines get a little blurry when dealing with more complex scenarios, for example overlapping objects with different types of event listeners. This tutorial explains exactly how Corona handles these events and which events are broadcast to which objects.

Tap vs. Touch

Let's quickly discuss the basics for users who may be new to Corona or mobile development in general. When the user touches the screen of a touch-sensitive device, this action is regarded in one of two ways:

tap — This event is represented by the user quickly touching the screen and lifting off at the same approximate point on the screen.

touch — These events provide for a much greater level of screen interactivity. Using touch events, you can detect when the user first touches the screen and when the touch is lifted off the screen. You can also track the motion of the touch as it moves around the screen.

This tutorial won't go into depth about the properties returned from these events — if you need to explore this topic further, please refer to the Tap/Touch/Multitouch guide.

Event Distinction

Looking beyond the basic concept of tap vs. touch, let's explore how Corona handles these events. At the core level, it's important to understand that each type is regarded as distinct. You may think that "a screen touch is just a screen touch," but the difference will become more apparent as we examine more complex examples.

Test Project Setup

For purposes of this tutorial, let's quickly set up a test project consisting of two squares that overlap in the center. We'll use these squares to test different types of listeners and explore how/when the events are processed.

Open the Corona Simulator.

Click New Project from the welcome window or select New Project... from the File menu.

For the project/application name, type TapTouch and ensure that the Blank template option is selected. Leave the other settings at default and click OK (Windows) or Next (Mac). This will create the basic files for the test project in the location (folder) that you specified.

Locate the project folder and open the main.lua file in your chosen text editor. Inside, replace the existing lines with this code:

Tap Over Tap

Working with this example, let's explore the most simple case of overlapping objects: one tap object overlapping another tap object. When you click/tap in the center section where the squares overlap and observe the output in the console, you'll notice that (by default) tap events transmit through — or "propagate" through — to underlying objects. In other words, the front (red) square doesn't block the tap event from reaching the back (blue) square, and the console reflects this:

Front Object TAP
Back Object TAP

This is by design, but how do you prevent this from occurring when such behavior is not desired? The solution is to simply return true at the end of the listener function for associated objects, in this case tapListener(). Doing so will prevent tap events on an object from propagating through to tap-able objects behind it.

In addition, let's change both squares to touch objects instead of tap objects and associate them with the touchListener() function. This can be done on lines 26 and 29 by changing "tap" to "touch" and changing tapListener to touchListener.

Tap/Touch Over Touch/Tap

Things get a little more complicated when you have objects with different listener types overlapping each other, but you still need to control the propagation of tap and touch events. For testing purposes, let's adjust the sample project so it becomes a tap object (red square) over a touch object (blue square). Do this by editing line 29, changing "touch" to "tap" and touchListener to tapListener:

Now refresh/reload the project and click/tap in the center section where the squares overlap. In the console, the output messages may look similar to this:

Back Object TOUCH (began)
Back Object TOUCH (ended)
Front Object TAP

Notice that the back square still receives touch events despite the fact that we've added return true to both the tapListener() and touchListener() functions. This is why it's important to understand, as noted earlier, that tap and touch events are actually distinct from Corona's standpoint, despite some similarities from the user's standpoint.

The behavior is similar if we change the sample project to a touch object over a tap object. To test this, edit the code as follows:

Refresh/reload the project and, once again, click/tap in the center section where the squares overlap. In the console, the output messages may look similar to this:

Front Object TOUCH (began)
Front Object TOUCH (ended)
Back Object TAP

Working with Overlaps

The above examples of overlapping objects with different listener types is fairly common in app development, so you'll need an approach to handle these situations. Some examples include:

Objects with tap listeners over a larger region that must detect touch events, for example basic tap-able objects placed over a background that can be scrolled/moved as the user touches and drags it.

A draggable touch object that can be moved around over underlying tap objects which the user can tap on to collect/activate.

For both of these cases, and numerous others, there is a tactic to prevent the "wrong type" of event from propagating through to an underlying object with a different listener type, as follows:

In the instance where a front object should behave as a tap object, and objects behind are touch objects, we can add a listener of both types (tap and touch) to the front object, with the "touch" event listener being simply an anonymous function which returns true:

In addition, we can use the :setFocus() method in the touchListener() function to give focus to the background object. This works nicely because, since we effectively blocked touch propagation from passing through the front object, any touch object behind it will only receive focus if the touch point is outside of the front object's bounds.

If the front object should behave as a touch object, and objects behind are tap objects, the same concept applies. Just add a listener of both types to the front object and define its "tap" event listener as an anonymous function which returns true:

Conclusion

Every project will vary slightly in the exact way that it should behave in regards to tap and touch, but understanding these core concepts is essential to the ultimate user experience. Experiment with the different types and phases, and always remember to test, test, and test again!