3

Hey I have a Good peice of code in my touches moved it was given to me by @Sam_M.

Im trying to basically keep track of the location of multiple fingers when there moving. Like Finger 1 is here and Finger 2 in there. So as of right now it will print the number of fingers/touches that are currently on the view and the location but It won't keep track of both individual fingers separately. Ive looked around at the only 3 other question that are on stackoverflow. But none gave me a good result i can implement in swift.

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {

for touch: UITouch in event!.allTouches()! {

for (index,touch) in touches.enumerate() {
    let ptTouch = touch.locationInNode(self.view)
    print("Finger \(index+1): x=\(pTouch.x) , y=\(pTouch.y)")
 }
}

}

override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {

for touch: UITouch in event!.allTouches()! {

for (index,touch) in touches.enumerate() {
    let ptTouch = touch.locationInNode(self.view)
    print("Finger \(index+1): x=\(pTouch.x) , y=\(pTouch.y)")
  }
 }
}
Hunter
  • 107
  • 1
  • 10

2 Answers2

7

The touch object for each finger will stay the same with the same memory address while it's on the screen. You can keep track of individual fingers in a multi touch scenario by storing the address of touch objects in an array and then comparing against that array to know exactly which finger is moving.

var fingers = [String?](count:5, repeatedValue: nil)

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    super.touchesBegan(touches, withEvent: event)
    for touch in touches{
        let point = touch.locationInView(self.view)
        for (index,finger)  in fingers.enumerate() {
            if finger == nil {
                fingers[index] = String(format: "%p", touch)
                print("finger \(index+1): x=\(point.x) , y=\(point.y)")
                break
            }
        }            
    }        
}

override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
    super.touchesMoved(touches, withEvent: event)
    for touch in touches {
        let point = touch.locationInView(self.view)
        for (index,finger) in fingers.enumerate() {
            if let finger = finger where finger == String(format: "%p", touch) {
                print("finger \(index+1): x=\(point.x) , y=\(point.y)")
                break
            }
        }
    }
}

override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
    super.touchesEnded(touches, withEvent: event)
    for touch in touches {
        for (index,finger) in fingers.enumerate() {
            if let finger = finger where finger == String(format: "%p", touch) {
                fingers[index] = nil
                break
            }
        }
    }        
}

override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
    super.touchesCancelled(touches, withEvent: event)
    guard let touches = touches else {
        return
    }
    touchesEnded(touches, withEvent: event)
}

Updated for swift 4

Credit to @Klowne

var fingers = [UITouch?](repeating: nil, count:5)

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    super.touchesBegan(touches, with: event)
    for touch in touches{
        let point = touch.location(in: self.view)
        for (index,finger)  in fingers.enumerated() {
            if finger == nil {
                fingers[index] = touch
                print("finger \(index+1): x=\(point.x) , y=\(point.y)")
                break
            }
        }
    }
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    super.touchesMoved(touches, with: event)
    for touch in touches {
        let point = touch.location(in: self.view)
        for (index,finger) in fingers.enumerated() {
            if let finger = finger, finger == touch {
                print("finger \(index+1): x=\(point.x) , y=\(point.y)")
                break
            }
        }
    }
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    super.touchesEnded(touches, with: event)
    for touch in touches {
        for (index,finger) in fingers.enumerated() {
            if let finger = finger, finger == touch {
                fingers[index] = nil
                break
            }
        }
    }
}

override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
    super.touchesCancelled(touches, with: event)
    guard let touches = touches else {
        return
    }
    touchesEnded(touches, with: event)
}

*As per apple's updated documentation it's now OK to retain touches during a multi touch sequence as long as they are released at the end of the sequence

Swifty
  • 3,730
  • 1
  • 18
  • 23
  • 2
    From [the documentation](https://developer.apple.com/reference/uikit/uitouch): **Never retain a touch object when handling an event. If you need to keep information about a touch from one touch phase to another, copy that information from the touch** – matt Oct 03 '16 at 03:03
  • Wooowww! Amazing it works incredibly Perfect!!! I do have a follow-up question. I want to attach a node to the finger #2 how do i simply say "node.position = finger2position" and also someone just down voted your question lmao i dont know why? but i up voted it to bring it back to par @Sam_M – Hunter Oct 03 '16 at 03:08
  • The way to identify a UITouch without retaining it is to take its memory address. I've added info to my answer showing how to do that. – matt Oct 03 '16 at 03:12
  • @Hunter I downvoted it because it does something that Apple says you must not do. You can store a touch's string identifier in an array, but not the touch itself. – matt Oct 03 '16 at 03:12
  • oh ok dang well. it does work in incredible fashion but if you want to play by the book then i see why you down voted it. Its all good man. @matt – Hunter Oct 03 '16 at 03:15
  • hey Sam can you update your answer with this line of code in matt's answer? @Sam_M – Hunter Oct 03 '16 at 03:21
  • @matt Thanks for your comment. I Updated my answer to use the memory addresses instead. – Swifty Oct 03 '16 at 03:32
  • @Hunter Updated the answer. – Swifty Oct 03 '16 at 03:32
  • 1
    This is absurd. The docs only say that, if you keep the record of a touch, then you must release it when the touch ends. You can and should retain any object that may be necessary, provided you take appropriate measures not to leak memory. – Luis Vieira Damiani Jan 04 '18 at 05:34
  • @LuisVieiraDamiani You do realize that this answer and comments are more than a year old right? At the time the bold part of matt's first comment was exactly what the documentation said, it must have been changed since then – Swifty Jan 05 '18 at 07:06
  • I did realize that when I wrote, and still think the idea of not being able to store an object is absurd. Perhaps Apple realized that too and changed the docs? In any case, your answer works perfectly, and the idea of storing a string as opposed to the object itself is immaterial, provided you set it to nil when the touch ends. I got mad that people made you go through unnecessary trouble, and even downvoted you due to lack of criticism toward Apple itself. They make mistakes, too. – Luis Vieira Damiani Jan 05 '18 at 19:07
  • while this approach works, it brings additional pain with leaks and edge cases - when touchesEnded or touchesCancelled are not invoked. or when touchesBegan is invoked twice. be careful! – ursa May 15 '20 at 09:32
3

A touch is a finger. A finger is a touch. You can identify a finger by identifying the touch; the touch for a particular finger will be identically the same object as long as that finger is touching the screen. To get a unique identifier for a particular UITouch, take its memory address, like this:

let uuid = String(format: "%p", theTouch)

You can store the unique identifier for each touch in a property. But do not store the touch itself; Apple forbids it.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • 1
    "Apple" does not forbid anything. You have a somewhat esoteric attitude toward the thing. The docs only say to release whatever you store, since new objects are created every time a touch begins, as not to leak memory. – Luis Vieira Damiani Jan 04 '18 at 05:44
  • 5
    "A touch object persists throughout a multi-touch sequence. You may store a reference to a touch while handling a multi-touch sequence, as long as you release that reference when the sequence ends. If you need to store information about a touch outside of a multi-touch sequence, copy that information from the touch." – Luis Vieira Damiani Jan 04 '18 at 06:35