5

I have implemented both single and double tap. But whenever I double tap the screen the single tap code is also run. Please see code below:

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    let touch = touches.first as UITouch!
    let touchLocation = touch?.location(in: self) 
    let touchedNode = self.atPoint(touchLocation!)

if touch?.tapCount == 1 {
        print("single tap")
        if let name = touchedNode.name        {
            if name == "enemySprite"
            {
                //handle single Tap
             }
         }
    }

if touch?.tapCount == 2 {
        print("double tap")
        if let name = touchedNode.name        {
            if name == "enemySprite"
            {
                //handle double Tap
             }
         }
    }
}

How can I make each of the tap counts mutually exclusive?

UPDATE

Based on Apurv’s answer and Whirlwind’s comment I thought the following code show work.

 override func didMove(to view: SKView) {

    var tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.view.sampleTapGestureTapped(recognizerMethod:)))
    self.view.addGestureRecognizer(tapGesture!)
    tapGesture.numberOfTapsRequired = 1;

    var tapGestureDouble = UITapGestureRecognizer(target: self, action: #selector(self.view.sampleTapGestureTapped(recognizerMethod2:)))
    self.view.addGestureRecognizer(tapGestureDouble!)
    tapGestureDouble.numberOfTapsRequired = 2;

    [tapGesture requireGestureRecognizerToFail : tapGestureDouble];

}

It doesn’t work and I get errors saying,

Value of type ’SKView?’ has no member ‘sampleTapGestureTapped(recognizerMethod:)’ and 
Value of type ’SKView?’ has no member ‘sampleTapGestureTapped(recognizerMethod2:)’

pointing to the declaration of the tap gesture variables

Also, I get the error

Expected expression in the container literal 

pointing to the last line in the above method.

I guess I’m missing something right here, I would really appreciate some help and clarifications on this.

ItsMeAgain
  • 595
  • 4
  • 18
  • The problem is that you're not single- or double-tapping the screen as iOS sees it, you're just touching the screen once or twice :-) Just as you would not use touchesBegan to detect a swipe by seeing there the first touch was and then trying to monitor the movement and direction of the touch. (although I think in early iOS days that's exactly what you had to do for taps, swipes, pinches, rotations etc - sounds a nightmare). – Steve Ives Mar 10 '17 at 14:42

2 Answers2

4

The better way to handle tap is to use gesture recogniser. Use UITapGestureRecognizer and set proper number of taps which you want to track.

Now to allow both taps to work seamlessly add code to make sure that your single gesture gets called only when your double tap fails. See the code below.

    var tapGesture = UITapGestureRecognizer(target: self, action: #selector(ViewController.sampleTapGestureTapped(recognizerMethod:)))
    self.view.addGestureRecognizer(tapGesture!)
tapGesture.numberOfTapsRequired = 1;

var tapGestureDouble = UITapGestureRecognizer(target: self, action: #selector(ViewController.sampleTapGestureTapped(recognizerMethod2:)))
    self.view.addGestureRecognizer(tapGestureDouble!)
tapGestureDouble.numberOfTapsRequired = 2;

[tapGesture requireGestureRecognizerToFail:tapGestureDouble];
Apurv
  • 17,116
  • 8
  • 51
  • 67
4

You don't have to access ViewController necessarily... You can handle taps inside you scene, like this:

import SpriteKit

class GameScene: SKScene {

    var recognizers:[UIGestureRecognizer] = []

    override func didMove(to view: SKView) {

        // Get label node from scene and store it for use later


        let single = UITapGestureRecognizer(target: self, action: #selector(GameScene.singleTapHandler(recognizer:)))
        view.addGestureRecognizer(single)
        single.numberOfTapsRequired = 1
        recognizers.append(single)

        let double = UITapGestureRecognizer(target: self, action: #selector(GameScene.doubleTapHandler(recognizer:)))
        view.addGestureRecognizer(double)
        double.numberOfTapsRequired = 2
        recognizers.append(double)

        single.require(toFail: double)

    }

    override func willMove(from view: SKView) {
        super.willMove(from: view)

        for recognizer in recognizers {
            self.view?.removeGestureRecognizer(recognizer)
        }
        recognizers.removeAll()
    }

    func singleTapHandler(recognizer:UITapGestureRecognizer){

        print("Single tap detected")
    }

    func doubleTapHandler(recognizer:UITapGestureRecognizer){
        print("Double tap detected")
    }

}

Of course calling some methods on ViewController can be done in a few ways (eg. delegation)... Still, based on your comments, I think that handling taps inside of a current scene is what you want to achieve.

EDIT:

To answer the question from your comment... You can grab the node which is touched by finding the touch location in view, converting it to the location in scene, and grabbing the node at that location:

func singleTapHandler(recognizer:UITapGestureRecognizer){

        print("Single tap detected")

        let locationInView = recognizer.location(in: self.view)

        let locationInScene = self.convertPoint(fromView: locationInView)

        print(locationInScene)

       let node = atPoint(locationInScene)

        if let nodeName = node.name {
            //do stuff
        }

        //or

        if let sprite = node as? SKSpriteNode {
            // node is an SKSpriteNode
        }
    }
Whirlwind
  • 14,286
  • 11
  • 68
  • 157
  • Thanks. The issue now is how do I determine which sprite was tapped? In my question above, I was using name property of an SKNode to determine the exact sprite that was touched. How can that be done within the UITapGestureRecognizer? – ItsMeAgain Mar 11 '17 at 11:06
  • @ItsMeAgain Checkout my edit. Also, keep in mind that if you have a question that differs from a title of a current question, you should ask a new one. – Whirlwind Mar 11 '17 at 13:21
  • @ItsMeAgain And you may find useful to know about this method (nodesAtPoint) : https://developer.apple.com/reference/spritekit/sknode/1483072-nodesatpoint – Whirlwind Mar 11 '17 at 13:22