2

In my project, I have the main view, in which I add a UITapGestureRecognizer, and inside this main view, I have a subview that is a custom UIControl, which I will call UICustomButton.

The UICustomButton overrides the following methods of UIControl:

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        pressAnimation()
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesEnded(touches, with: event)
        releaseAnimation()
        listener?.onClick(sender: self)
    }

    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesCancelled(touches, with: event)
        releaseAnimation()
    }

The problem I am having is, all "click touches" are hitting the following callbacks:

  • touchesBegan
  • touchesCancelled

The touchesEnded callback is not being called, it's kinda being ignored and I don't know why.

How can I make touchesEnded be called instead of touchesCancelled on a touch action?


Some facts:

  • if I remove the UITapGestureRecognizer from the parent view, everything works fine;
  • even not calling the supers and overriding all touches methods, the touchesCancelled is called =/;
  • if I do a "long touch" or do a "big moving gesture", touchesEnded is called :o.
Augusto Carmo
  • 4,386
  • 2
  • 28
  • 61

1 Answers1

7

This is the correct behaviour for a view that has a gesture recogniser attached.

The UIGestureRecognizer documentation says "If a gesture recognizer recognizes its gesture, the remaining touches for the view are cancelled":

https://developer.apple.com/documentation/uikit/uigesturerecognizer

The property cancelsTouchesInView (which defaults to true), determines whether or not touches are cancelled when a gesture is recognized:

https://developer.apple.com/documentation/uikit/uigesturerecognizer/1624218-cancelstouchesinview

Because a long touch and a swipe are not recognized by a tap recognizer, it doesn't interfere with them. It only intervenes when it recognizes a tap.

If you set the recognizer's cancelsTouchesInView property to false, then the touches shouldn't be cancelled, and the touchesEnded(_:with:) method will be called as usual.

You can set that property either in code or in Interface Builder (if you added your gesture recognizer by dragging it out in your storyboard).

Pete Morris
  • 1,472
  • 8
  • 13
  • Hello, @PetMorris. Thank you very much for your detailed answer. It worked, `touchesEnded()` is now being called \o/. But the problem I am having now is that both callbacks, `touchesEnded()` and the one from the `UITapGestureRecognizer` from the parent view are being fired. Is there any way I can make only the `UICustomButton`\`s callback to be called? Thanks o/ – Augusto Carmo Sep 04 '18 at 15:58
  • 1
    You’ll have to assign a delegate to your recognizer. Check out UIGestureRecognizerDelegate. Its gestureRecognizer(_:shouldReceive:) method returns a Bool which states whether or not the touch should be handled by the recognizer. You’ll need to judge the location of the touch and if it’s within the bounds of your custom control return false from this delegate method. – Pete Morris Sep 04 '18 at 17:02
  • Thanks very much for the reply, @PeteMorris. That's what I've done. But I was wondering if there were another ways to do that. I'll stick to that solution then. Cheers \ o / – Augusto Carmo Sep 05 '18 at 08:55