Handling touch events in SpriteKit is not as trivial as one might expect. Especially when two or more nodes are on top of each other. Which node will receive the touch event? In this article I will explain how touch events are handled and that this can lead to unexpected behaviour.
Situation
For a game I wanted a configuration button that expands when one pushes on it. For a better understanding let’s have a look at the following images. The first image is the configuration button in the initial state.
The next image is the configuration button in the expanded state.
Sounds easy enough, right?
For the buttons in my game I created a class ButtonNode (simular to this one) that extends SKSpriteNode and implements the methods touchesBegan, touchesMoved and touchesEnded so it can receive touch events. I added the buttons on the screen and added an SKAction to move the configuration bar to the right when the button is clicked. When I started the application, and pushed the button … I was redirected to the title screen??
Rendering Order != Hit Testing Order
So what happened? In the initial state the home button lies underneath the configuration button. Apparently the touch event was handed to this home button instead of to the configuration button. According to the documentation, the hit testing order is the reverse of the drawing order. I tried to switch the order of adding the 2 buttons to the scene, but this did not work either. This is probably because I have set ignoresSiblingOrder to true, which kind of makes it non deterministic in which order the buttons are drawn.
The next solution I came up with, was to set the property userInteractionEnabled on the home button to false in the initial state. This will prevent the home button from receiving touch events. Again I touch the configuration button and … nothing happens??
Touch events are propagated to parent node
Isn’t this strange? The only button that is configured to receive touch events is the configuration button. So you expect this button to receive all the events. The truth is that when two nodes lie on top of each other, only one of them will receive the touch event. If this node is not configured to handle touch event, the events are propagated to its parent, or the parent of its parent, until a parent is found that will handle the events. In this case the parent of the home button is the configuration bar. The parent of the configuration bar is the SKScene. So the SKScene will receive the touch event and not the configuration button as we had hoped.
Solutions
Now that we know how this works, we can easily create a solution. The solutions I came up with are:
* Write some code in SKScene to ‘hand over’ the touch event to the configuration button
* Add the configuration bar as child of configuration button
Hope you guys found my page before hours of searching like I did. If I helped you with my article or if you have any questions, let me know.