Search code examples
iosswiftxcodeuitouch

How to track multiple touches


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)")
  }
 }
}

Solution

  • 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