4

I want to clean up my touchesBegan(..) in SKScene. I wanted to make a case statement instead of my if .. else chain. However, I get errors when implementing, which suggests that I don't know how equality is done under the hood.

Before code:

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    for touch: AnyObject in touches {
        let location = touch.locationInNode(self)

        if self.nodeAtPoint(location) === playLabel {
            buildLevelSelect()
        } else if self.nodeAtPoint(location) === aboutLabel {
            createAboutView()
        } else if self.nodeAtPoint(location) === storeLabel {
            createStore()
        }
    }
}

After code: After clicking around, some label clicks work, but some others raise a Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode 0x0) error:

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    for touch: AnyObject in touches {
        let location = touch.locationInNode(self)

        switch(self.nodeAtPoint(location)){
        case playLabel :
            buildLevelSelect()
        case aboutLabel :
            createAboutView()
        case storeLabel :
            createStore()
        default : break
    }
}
cdpalmer
  • 658
  • 1
  • 7
  • 19

3 Answers3

5

You can write a sort of odd looking but functional switch if you want the === behavior as follows:

switch(true){
        case self.nodeAtPoint(location) === playLabel : buildLevelSelect()
        case self.nodeAtPoint(location) === aboutLabel : createAboutView()
        case self.nodeAtPoint(location) === storeLabel : createStore()
        default : break
}

In my opinion, this is still cleaner looking than the if-else chain.

Daniel Legler
  • 527
  • 1
  • 6
  • 16
  • 1
    Ohh... thinking outside the box. I like this. I'll wait a day or two to feel out others before accepting an answer. – cdpalmer Mar 03 '17 at 17:49
4

The easiest way to accomplish this would be by populating the .name property of your nodes, so then you could switch on the name instead. So then your switch would look like this:

switch (self.nodeAtPoint(location).name ?? "") { 
    case "playLabel" :
        buildLevelSelect()
    case "aboutLabel" :
        ...
creeperspeak
  • 5,403
  • 1
  • 17
  • 38
  • 1
    **This is the best way to do it.** @cdpalmer You should check if a string is equal to another string (name), not compare two object that could be different also if you use the binary operator === (for example two string with the same return a crash if one is String and the other is NSString) ... – Alessandro Ornano Mar 04 '17 at 07:58
  • This makes sense. I would have to do some refactoring, I only figured out you could tie string names to objects halfway through my side project. – cdpalmer Jun 07 '17 at 18:00
3

You can also cast the objects in the switch statement and then your code works as expected

    if let touch = touches.first as UITouch! {

        let touchLocation = touch.location(in: self)

        switch self.atPoint(touchLocation) {
            case redBox as SKSpriteNode:
                print("touched redBox")
            case greenBox as SKSpriteNode:
                print("touched greenBox")
            default:
                print("touched nothing")
        }
    }
Ron Myschuk
  • 6,011
  • 2
  • 20
  • 32