how does UIGestureRecognizer works with UIResponder chain?
Asked Answered
B

2

10

I know that touch events can delivery to hit-view or gesture. but something confused me in my demo:

I have two subviews in my root view, one is testView (subclass of UIView), the other is testBtn(UIButton) which action is "testBtnClicked". Then, I add an UITapGestureRecognizer to my root view and set its action to "tapAction".

The problem comes:

when I touch on root view , it trigger "touchsBegan:withEvent:" 、 "tapAction" and "touchsCancelled:withEvent" method. That's what I expected;

When I touch on testView, it works the same as above;

when I touch on testBtn, it only triggered "testBtnClicked" method.Why?

why the gesture is recognized when I touch on testView not the root view? and why it's not recognized when I touch on testBtn?

Between answered 12/12, 2016 at 9:49 Comment(0)
B
15

Aha! May be I got the answer:

Firstly, gestureRecognizers get the first opportunity to recognize a touch,as the App Doc says:

A window delays the delivery of touch objects to the view so that the gesture recognizer can analyze the touch first. During the delay, if the gesture recognizer recognizes a touch gesture, then the window never delivers the touch object to the view, and also cancels any touch objects it previously sent to the view that were part of that recognized sequence.

Secondly,In iOS 6.0 and later,UIButton actions prevent overlapping gesture recognizer behavior, also as the Apple Doc says:

In iOS 6.0 and later, default control actions prevent overlapping gesture recognizer behavior. For example, the default action for a button is a single tap. If you have a single tap gesture recognizer attached to a button’s parent view, and the user taps the button, then the button’s action method receives the touch event instead of the gesture recognizer. This applies only to gesture recognition that overlaps the default action for a control, which includes:

  • A single finger single tap on a UIButton, UISwitch, UIStepper, UISegmentedControl, and UIPageControl.
  • A single finger swipe on the knob of a UISlider, in a direction parallel to the slider.
  • A single finger pan gesture on the knob of a UISwitch, in a direction parallel to the switch.

If you have a custom subclass of one of these controls and you want to change the default action, attach a gesture recognizer directly to the control instead of to the parent view. Then, the gesture recognizer receives the touch event first. As always, be sure to read the iOS Human Interface Guidelines to ensure that your app offers an intuitive user experience, especially when overriding the default behavior of a standard control.

Between answered 13/12, 2016 at 3:51 Comment(0)
C
0

I know this is a few years late, but an important piece of information seems to be missing in the answer: UIButton uses the UIView's instance method gestureRecognizerShouldBegin(_:) to stop the tracking of touch events.

When the instances of UITouch and UIEvent are handed to the UIButton by UIKit, the UIGestureRecognizer.State is still at .possible and has not yet been fully recognized. As the OP mentioned, UIGestureRecognizer gets to examine the touch objects first, but by setting gestureRecognizerShouldBegin(_:) to return false, UIButton thinks the .possible state has transitioned to .failed and refrain from climbing up the UIGestureRecognizer chain, which would've been the root view in the above example. And this is why the UIGestureRecognizer in the root view is not triggered.

With the UIGestureRecognizer out of the way, the default action for UIButton gets triggered.

Chouinard answered 4/12, 2020 at 5:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.