Search code examples
iosswiftxcodeskspritenodesknode

Why does Xcode TouchesEnded() topMost touched node always return child nodes rather than the parent node?


I have Subclassed SKSpriteNode as a MenuButton()

The MenuButton() contains 2 children:

  • SKSpriteNode (an Image)

  • SKSpriteNode (some text)

When the below runs, if the user touches part of the button containing the image or the text (the children of the MenuButton() ), it doesn't execute as the image/text have a different name to the button and they are the first touchedNode.

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {

        guard let touch = touches.first else { return }
        let location = touch.location(in: self)
        let touchedNode = atPoint(location)
        let touchedNodeName = atPoint(location).name

        if let nodeName = touchedNodeName, nodeName == ("btnPlay") { self.showGameModeMenu() }
}

Previously, I used:

let touchedNodes = nodes(at: location)
for node in nodes {
    if let nodeName = node.name, nodeName == "btnPlay" { self.showGameModeMenu() }
}

However, when another node (btnCloseLeaderboard) is presented over the top btnPlay, it loops through and executes both rather than just the top-most node (btnCloseLeaderboard)

I've attempted to set isUserInteractionEnabled = false for the child nodes but that doesn't work. It still sees then as the topmost node.

How do I prevent this from happening?


Solution

  • It sounds like you to want to show the game menu if the name of a child's parent is "btnPlay" or if the node named "btnPlay" is tapped itself (for taps outside the image or text children). If so, you can change your top code to this:

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else { return }
        let location = touch.location(in: self)
        let touchedNode = atPoint(location)
        if touchedNode.parent?.name == "btnPlay" || touchedNode.name == "btnPlay" { showGameModeMenu() }
    }