-
-
Notifications
You must be signed in to change notification settings - Fork 1k
Return false if gesture recognizers are present but all disabled #3377
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Return false if gesture recognizers are present but all disabled #3377
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @jcolicchio! Thanks for submitting this PR! I have some remarks though:
- Let's use
YES
andNO
instead oftrue
/false
. I'm also not a fan of this, but for now I'd like to stay consistent with our codebase. - I think that this PR will break
macos
. - Before we merge it, we have to test those changes, so please provide a reproduction that we can use
apple/RNGestureHandlerButton.mm
Outdated
// touch handling. Therefore, as a bandaid we can check to ensure we don't return | ||
// true here if the only gesture recognizers we have are all disabled. | ||
BOOL hasEnabledGestureRecognizer = false; | ||
for (UIGestureRecognizer *gestureRecognizer in view.gestureRecognizers) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty sure that this will break on macos
😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should I go ahead and move this implementation into #if !TARGET_OS_OSX
for the sake of this fix, which may be an iOS-specific issue to begin with?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I would prefer something like this:
#if !TARGET_OS_OSX
for (UIGestureRecognizer *gestureRecognizer in view.gestureRecognizers) {
#else
for (NSGestureRecognizer *gestureRecognizer in view.gestureRecognizers) {
#endif
Now when I checked this macos
did compile without issues, but I'm not sure why it does recognize UIGestureRecognizer
if it has its own NSGestureRecognizer
, so I believe it is better to split it into two parts. Also making it iOS
exclusive may not be the best as it may also occur on macos
(but for that we would need a reproduction)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it, I'll take a look at such an implementation tomorrow
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually went with an implementation using NSPredicate
to filter on isEnabled == YES
, which I believe should work equally well on OSX as iOS? I don't have a good test setup to validate on OSX though, so please take a look and let me know if it works alright.
I also updated the comment, and confirmed that my snippet indeed produces the bug on 2.22.0 (Gestures' Text was unfortunately a non-starter), and that this proposed change addresses the bug using Xcode's debugger, so I think this PR should be good to go! Requesting re-review
apple/RNGestureHandlerButton.mm
Outdated
@@ -64,10 +64,24 @@ - (BOOL)shouldHandleTouch:(RNGHUIView *)view | |||
return button.userEnabled; | |||
} | |||
|
|||
// Certain subviews such as RCTViewComponentView have been observed to have disabled | |||
// accessibility gesture recognizers such as _UIAccessibilityHUDGateGestureRecognizer, | |||
// ostensibly set by iOS. Such gesture recognizers cause us to return early, despite |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if "return early" is correct in that case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is incorrect. In my case, the handler for a touch returned by this algorithm was the native representation of <View accessible accessibilityRole="button"><Text>Static Text</Text></View>
. It didn't have any valid tap gesture recognizers to handle touch events, but it was still chosen by this algorithm to be the view which "shouldHandleTouch"
If the algorithm does not return this child view "early", it instead traverses up to the parent RNGestureHandlerButton
and returns the parent view, which does have gesture handlers due to, in this case, my BorderlessButton
's onPress
, which faithfully gets invoked on tap
So, I think this algorithm should not return a child view which happens to have two disabled accessibility gesture recognizers, which were mysteriously added from some unknown source, just because the number of gesture recognizers is greater than zero. Instead, I think it should ignore these disabled gesture recognizers when determining which view should handle touch, so that the parent RNGestureHandlerButton
can instead be returned and handle touches
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see what you mean. What I meant was that the way it is phrased in this comment suggests something like early return in this specific function, not in the whole process.
(...) view which happens to have two disabled accessibility gesture recognizers, which were mysteriously added from some unknown source (...)
My guess is that they are added because of View
with accessibilityRole="button"
, but I have not checked that yet.
Anyway, I think that we should rephrase this comment when we have more information
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, do you use Text
from React Native, or from Gesture Handler?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll rephrase the comment to clarify. I find it odd that only in certain scenarios do certain View
s get these accessibility tap gestures added to them
And, I'm using Text
from React Native, how would I import Text
from Gesture Handler? I'm seeing Module '"react-native-gesture-handler"' has no exported member 'Text'
when I try
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I remember Text
component was introduced form 2.22. I asked because that could explain why those views have additional gesture recognizers.
I've checked example with only View
with props accessible
and accessibilityRole="button"
and it seems that it doesn't have any additional recognizer by default
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm... Using Text
from RNGH 2.22 actually causes the button to not be tappable at all, as in the following:
<AnimatedBorderlessButton onPress={() => onSelect(item)}>
<View accessible accessibilityRole="button">
<Text style={styles.text}>{item}</Text>
</View>
</AnimatedBorderlessButton>
According to the debugger, the RCTParagraphTextView
has a RNDummyGestureRecognizer
initially, even before taking the steps which produce the bug in React Native's Text
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to the debugger, the RCTParagraphTextView has a RNDummyGestureRecognizer initially, even before taking the steps which produce the bug in React Native's Text
That's exactly what I meant by:
I asked because that could explain why those views have additional gesture recognizers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it — I’d thought you might have been suggesting that NOT using that particular Text might explain why I was seeing additional gesture recognizers
|
The code looks ok, have you been able to prepare a reproduction? It would be great to check what causes this issue 😅 |
I added it to the description of the PR, at the bottom inside of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've just tested that on the repro that you've provided and it indeed works 😅
What's interesting, it does work inside our example app without this fix. I thought it may be related to React old/new architectures, but it seems that it is not a case.
Also, I took the liberty of slighgtly changing comment, hope you don't mind 😅
Description
This PR adjusts
shouldHandleTouch
to check for enabled gesture recognizers instead of returning true if the count of gesture recognizers is greater than zero.The motivation behind this PR is to address a bug where buttons become unresponsive if, I guess, iOS is inserting disabled accessibility gesture recognizers into the child views??
Fixes #3376
Test plan
I was able to reproduce this reliably on a private repo, and used the debugger to observe
shouldHandleTouch
returning too early from a descendant of the button meant to be tapped. With this fix, I confirmed that the correct button returns to handle to touch event and the buttons all behave as expectedI also was able to reproduce the issue with this sample code:
Courses
Hello World 1
All Courses
The
All Courses
button has anonPress
whichalert
s, and in this snippet, I observed no alert occurringClick to expand