touchesEnded not detecting individual fingers

Not sure how to format code on this forum …

I have the following code which for the most part works. However
touchesEnded only fires when ALL fingers are removed. This is inside a
UIInputViewController and the view has multiTouchEnabled = true. The touchesBegin does fire for each finger down.

How do I make it work so I get touchesEnded as each finger is removed?

var activeTouches = [UITouch: Key]()

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    super.touchesBegan(touches, withEvent: event)
    for touch in touches {
        let pressPoint = touch.locationInView(self.view)
        if let key = self.view.hitTestKey(pressPoint, withEvent: nil) as? Key {
            activeTouches[touch] = key
            key.sendActionsForControlEvents(.TouchDown)
            keyboardClick()
        }
    }
}

override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
    super.touchesMoved(touches, withEvent: event)
    for touch in touches {
        let pressPoint = touch.locationInView(self.view)
        let key = self.view.hitTestKey(pressPoint, withEvent: nil) as? Key
        
        if activeTouches[touch] != key {
            if let previousKey = activeTouches[touch] {
                previousKey.sendActionsForControlEvents(.TouchCancel)
            }
            
            if key != nil {
                activeTouches[touch] = key
                key!.sendActionsForControlEvents(.TouchDown)
            } else {
                activeTouches.removeValueForKey(touch)
            }
        }
    }
}


override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
    super.touchesEnded(touches, withEvent: event)
    for touch in touches {
        if let key = activeTouches[touch] {
            key.sendActionsForControlEvents(.TouchUpInside)
            activeTouches.removeValueForKey(touch)
        }
    }
}

override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
    super.touchesCancelled(touches, withEvent: event)
    if touches == nil {
        for key in activeTouches.values {
            key.sendActionsForControlEvents(.TouchCancel)
        }
        activeTouches.removeAll()
    } else {
        for touch in touches! {
            if let key = activeTouches[touch] {
                key.sendActionsForControlEvents(.TouchCancel)
                activeTouches.removeValueForKey(touch)
            }
        }
    }
}

Do any of the functions fire when one of the fingers is lifted? I would have expected touchesEnded to fire, if touchesBegan fires once for each finger as they are detected. What about touchesMoved/touchesCancelled? You might need to handle the touch state manually as long as you can get some notification that a touch has ended.

If you can’t you might need to subclass UIWindow and handle things at an even more basic level. Stack overflow has a code sample showing how to do this - it won’t be easy but if you absolutely must have it this may be the only way.

1 Like

Thanks for the suggestions. This is a keyboard extension and I’m trying to support users sliding their fingers onto different keys (UIButton). I have a UIInputView with multitouch enabled. All the keys have userinteraction disabled. So the code in UIInputView is responsible for sending events to the keys.

What I have noticed is that when 1) Touch finger down on one key, 2) touch another finger down on another key, 3) move second finger onto a third key then touchesEnded fires correctly, ie each time a finger is lifted. If I dont move 2nd finger onto another key then touchesEnded fire once only when all fingers have been removed.

I tried commenting out all the code in touchesMoved and it does change what I see above.

Any ideas please?

A little more information … I commented out all the overrides with the exception of touchesEnded and the results was still the same.

touchesEnded only fires immediately provided the second finger has moved a significant amount.

If I don’t move the second finger, then touchesEnded is delayed until all fingers are removed and then touchesEnded is fired for each finger.

OK - finally figured this out. I have a UIPanGestureRecognizer that is associated with the view.
When I set delaysTouchesEnded = false for the gesture then it works as expected.

However what I don’t understand is that in gestureRecognizerShouldBegin I am returning false.
So I would expect the above to only have an effect when this is true.

Am I missing something here?

If you return false here it just means the gesture recogniser enters the UIGestureRecognizerStateFailed state. To me that suggests it will not do anything else… but! the docs then say that when the gesture recogniser is in this state, “No action message is sent and the gesture recognizer is reset to UIGestureRecognizerStatePossible.”

  • in short, it doesn’t cancel all possible recognition…