Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift SKSpriteNode: Detect Tap / DoubleTap / LongPress

I'm attempting to create a subclass of SKSpriteNode which can detect user interaction (tap, double tap and hold), then defer to a delegate. This seems to me like a relatively common need, but:

  1. The code is incapable of detecting a double-tap without also triggering a single-tap.
  2. I haven't found a way to detect hold/long-press actions at all.

Am I going about this all wrong? I can't help feeling that SpriteKit should have something standard to handle something so basic. I know there's UIGestureRecognizer, but it doesn't seem very compatible with specific SKNodes rather than UIViews.

Here's what I have currently. Based on examples from Apple, I have all of the non-device-specific code in the main class file:

import SpriteKit

enum ActionType: Int {
    case Single
    case Double
    case Hold
}

protocol EnvironmentElementDelegate {
    func handleActionOnElement(element: EnvironmentElement, actionType: ActionType)
}

class EnvironmentElement: SKSpriteNode {

    let delegate: EnvironmentElementDelegate!

    init(imageNamed: String, elementNamed: String, delegate: EnvironmentElementDelegate) {
        self.delegate = delegate
        let texture = SKTexture(imageNamed: imageNamed)
        super.init(texture: texture, color: UIColor.clearColor(), size: texture.size())
        self.name = elementNamed
        userInteractionEnabled = true
    }

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}

...then all of the iOS-specific code in a separate extension file:

import SpriteKit

extension EnvironmentElement {

    override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
        for touch: AnyObject in touches {
            if (touch.tapCount >= 2) {
                NSObject.cancelPreviousPerformRequestsWithTarget(self)
            }
        }
    }

    override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
        for touch: AnyObject in touches {
            if (touch.tapCount == 1) {
                delegate.handleActionOnElement(self, actionType: ActionType.Single)
                // Unable to find Swift equivalent to this: [self performSelector:@selector(onFlip) withObject:nil afterDelay:0.3];
            } else {
                delegate.handleActionOnElement(self, actionType: ActionType.Double)
            }
        }
    }
}
like image 251
TheNeil Avatar asked Sep 28 '14 21:09

TheNeil


2 Answers

You can simply use touch.tapCount inside touchesBegan:

like image 179
topclaudy Avatar answered Nov 10 '22 10:11

topclaudy


I never had to detect a double tap, but if I have I will do it like this:

1 - add a local var named alreadyTouch and set it to false when the class is initialized

2 - inside touchesBegin if alreadyTouch is false set it to true else do what you have to

3 - setup a local Timer of 0.2 seconds and add also this to the if else to detect if the touch is rapid enough or not. so start this timer where you set alreadyTouch = true.

Hope it helps

like image 23
Totka Avatar answered Nov 10 '22 11:11

Totka