I need to make a sprite node to ignore touch, so that it fall through to other nodes behind it (lower z order).
In UIKit this can be done by overriding hitTest, but there doesn't seem to be a way here in SpriteKit. Another approach in UIKit is to change the zPosition of the CALayer. This way we can setup the view such that its zOrder is at the top (so that it renders on top), but it's touch behavior is still based on the relative order in superview's subviews array. Basically, we can decouple the touch handling and rendering order. I dont think we can do that in SpriteKit either.
Note that setting isUserInteractionEnabled = false doesn't work, since it still swallows the touch and prevent the nodes behind to receive touch event.
Anyone who used cocos2d before - this is basically the swallow touch flag.
My use case is that, I have a particle effect added to the screen during game play (it's added to the top of screen). Currently the particle swallows touches on its region, and affect my game play. I want to just show the particle, but do not interfere with touches for other components on screen.


I want to thank @Luca for the 2 solutions. They are very close to what I want, and inspired me with my final solution.
Luca's first solution has a few issues:
Luca's 2nd solution addressed some of these, but also has a few issues. For example, we have to introduce a new flag
isUserInteractionEnabled_2, and also need to traverse the scene to find the top most node. Also, it requires me to change the way I write existing games (rather than simply an infra change). So I strongly prefer Luca's 1st solution, because I can completely encapsulate the logic in my infra module, so all my games benefit from it without any change in game logic.So I improved Luca's 1st solution, and here's how I address the above 3 problems.
the exiting
pointAtAPI doesn't work as I explained above. Looks like tree traversal is unavoidable. So here it is:This code does a DFS traversal to find the node with max global Z (accumulating all the z's in the path).
I address this problem by keeping the particle (but making it invisible), until it's done its job to relay touch ended/cancelled events.
Here's how I do it:
Luckily, the internal implementation of SKEmitterNode is objc (rather than swift), so that I can overwrite functions (
touchesBegan, etc) in the extension.However, it's still better to subclass, in case we have a scenario where we do want to swallow touch. So I am keeping my another question open: SpriteKit unable to unarchive an SKEmitterNode subclass from sks file
Here's a complete implementation:
Note that I have a few helpers in other files, for example,
visibleis simply opposite ofisHidden, andzis simplyzPosition