How to recognize continuous touch in Swift?

The basic steps

  1. Store the location of the touch events (touchesBegan/touchesMoved)
  2. Move sprite node toward that location (update)
  3. Stop moving the node when touch is no longer detected (touchesEnded)

Here's an example of how to do that

Xcode 8

let sprite = SKSpriteNode(color: SKColor.white, size: CGSize(width:32, height:32))
var touched:Bool = false
var location = CGPoint.zero

override func didMove(to view: SKView) {
    /* Add a sprite to the scene */
    sprite.position = CGPoint(x:0, y:0)
    self.addChild(sprite)
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    touched = true
    for touch in touches {
        location = touch.location(in:self)
    }
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    for touch in touches {
        location = touch.location(in: self)
    }
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    // Stop node from moving to touch
    touched = false
}

override func update(_ currentTime: TimeInterval) {
    // Called before each frame is rendered
    if (touched) {
        moveNodeToLocation()
    }
}

// Move the node to the location of the touch
func moveNodeToLocation() {
    // Compute vector components in direction of the touch
    var dx = location.x - sprite.position.x
    var dy = location.y - sprite.position.y
    // How fast to move the node. Adjust this as needed
    let speed:CGFloat = 0.25
    // Scale vector
    dx = dx * speed
    dy = dy * speed
    sprite.position = CGPoint(x:sprite.position.x+dx, y:sprite.position.y+dy)
}

Xcode 7

let sprite = SKSpriteNode(color: SKColor.whiteColor(), size: CGSizeMake(32, 32))
var touched:Bool = false
var location = CGPointMake(0, 0)

override func didMoveToView(view: SKView) {
    self.scaleMode = .ResizeFill
    /* Add a sprite to the scene */
    sprite.position = CGPointMake(100, 100)
    self.addChild(sprite)
}

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    /* Start moving node to touch location */
    touched = true
    for touch in touches {
        location = touch.locationInNode(self)
    }
}

override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
    /* Update to new touch location */
    for touch in touches {
        location = touch.locationInNode(self)
    }
}

override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
    // Stop node from moving to touch
    touched = false
}

override func update(currentTime: CFTimeInterval) {
    /* Called before each frame is rendered */
    if (touched) {
        moveNodeToLocation()
    }
}

// Move the node to the location of the touch
func moveNodeToLocation() {
    // How fast to move the node
    let speed:CGFloat = 0.25
    // Compute vector components in direction of the touch
    var dx = location.x - sprite.position.x
    var dy = location.y - sprite.position.y
    // Scale vector
    dx = dx * speed
    dy = dy * speed
    sprite.position = CGPointMake(sprite.position.x+dx, sprite.position.y+dy)

}

The most difficult thing about this process is tracking single touches within a multitouch environment. The issue with the "simple" solution to this (i.e., turn "istouched" on in touchesBegan and turn it off in touchesEnded) is that if the user touches another finger on the screen and then lifts it, it will cancel the first touch's actions.

To make this bulletproof, you need to track individual touches over their lifetime. When the first touch occurs, you save the location of that touch and move the object towards that location. Any further touches should be compared to the first touch, and should be ignored if they aren't the first touch. This approach also allows you to handle multitouch, where the object could be made to move towards any finger currently on the screen, and then move to the next finger if the first one is lifted, and so on.

It's important to note that UITouch objects are constant across touchesBegan, touchesMoved, and touchesEnded. You can think of a UITouch object as being created in touchesBegan, altered in touchesMoved, and destroyed in touchesEnded. You can track the phase of a touch over the course of its life by saving a reference to the touch object to a dictionary or an array as it is created in touchesBegan, then in touchesMoved you can check the new location of any existing touches and alter the object's course if the user moves their finger (you can apply tolerances to prevent jitter, e.g., if the x/y distance is less than some tolerance, don't alter the course). In touchesEnded you can check if the touch in focus is the one that ended, and cancel the object's movement, or set it to move towards any other touch that is still occurring. This is important, as if you just check for any old touch object ending, this will cancel other touches as well, which can produce unexpected results.

This article is in Obj-C, but the code is easily ported to Swift and shows you what you need to do, just check out the stuff under "Handling a Complex Multitouch Sequence": https://developer.apple.com/library/ios/documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/multitouch_background/multitouch_background.html