Repository navigation
Commit 9703a2a
authored
[iOS] Preserve disabled button targets under box-none parents (#4506)
## Description
Follow-up to #4495.
When an RNGH `Touchable`/`Pressable` uses `pointerEvents="box-none"`, a
disabled
RNGH button returned from one of its child hit tests is rejected by
`shouldHandleTouch:` because `userEnabled` is false.
The `box-none` branch then continues checking lower siblings, which
allows an
enabled sibling underneath the disabled control to receive the touch.
This is inconsistent with the behavior preserved in #4495, where a
disabled
button can still remain the hit target so controls underneath don't take
over
the touch, while the disabled button itself doesn't activate.
The `box-none` path now preserves `RNGestureHandlerButton` child hits
instead
of discarding them based on their enabled state.
## Device observations
Reproduced on iOS with both RNGH `Touchable` and RNGH `Pressable`.
With an RNGH parent using `pointerEvents="box-none"`:
- tapping the disabled foreground triggers the enabled sibling
underneath
- tapping the exposed background also triggers the background, as
expected
Changing only the parent to a React Native `View` with the same
`pointerEvents="box-none"`:
- tapping the disabled foreground does nothing
- tapping the exposed background triggers the background
<details>
<summary>Test example</summary>
```tsx
import React, { useState } from 'react';
import {
Pressable as RNPressable,
StyleSheet,
Text,
View,
} from 'react-native';
import {
Pressable as RNGHPressable,
Touchable,
} from 'react-native-gesture-handler';
type ButtonType = 'touchable' | 'pressable';
export default function BoxNoneDisabledTargetExample() {
const [buttonType, setButtonType] = useState<ButtonType>('touchable');
const [useRNParent, setUseRNParent] = useState(false);
const [backgroundPresses, setBackgroundPresses] = useState(0);
const Button =
buttonType === 'touchable'
? Touchable
: RNGHPressable;
const children = (
<>
<Button
onPress={() => setBackgroundPresses((count) => count + 1)}
style={[StyleSheet.absoluteFillObject, styles.background]}>
<Text>Enabled background</Text>
</Button>
<Button
disabled
style={styles.disabledForeground}>
<Text>Disabled foreground</Text>
</Button>
</>
);
return (
<View style={styles.container}>
<RNPressable
onPress={() =>
setButtonType((current) =>
current === 'touchable' ? 'pressable' : 'touchable'
)
}>
<Text>Button type: {buttonType}</Text>
</RNPressable>
<RNPressable onPress={() => setUseRNParent((value) => !value)}>
<Text>
Parent: {useRNParent ? 'React Native View' : 'RNGH button'}
</Text>
</RNPressable>
{useRNParent ? (
<View pointerEvents="box-none" style={styles.frame}>
{children}
</View>
) : (
<Touchable pointerEvents="box-none" style={styles.frame}>
{children}
</Touchable>
)}
<Text>Background presses: {backgroundPresses}</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
padding: 16,
gap: 12,
},
frame: {
height: 150,
},
background: {
backgroundColor: '#9fd8a8',
justifyContent: 'flex-end',
padding: 8,
},
disabledForeground: {
position: 'absolute',
left: 40,
right: 24,
top: 20,
height: 85,
backgroundColor: '#9a9a9a',
alignItems: 'center',
justifyContent: 'center',
},
});
```
</details>
## Test plan
Test on a physical iOS device with both RNGH `Touchable` and RNGH
`Pressable`.
For each component:
1. Use an RNGH parent with `pointerEvents="box-none"`.
2. Tap well inside the disabled foreground.
3. Verify the enabled background does **not** receive `onPress`.
4. Tap the exposed background area.
5. Verify the background does receive `onPress`.
6. Replace only the parent with a React Native `View` using
`pointerEvents="box-none"` and repeat.
Expected:
| Parent | Disabled foreground | Exposed background |
| --- | --- | --- |
| RNGH `box-none` parent | no press | background press |
| RN `View` `box-none` parent | no press | background press |
Before this change, the RNGH-parent case incorrectly sends the disabled
foreground tap to the background sibling.
## AI assistance
Used AI to help inspect the surrounding hit-test paths and prepare the
test
case and patch. I personally reproduced the issue on device.1 parent 1c8af36 commit 9703a2a
1 file changed
Lines changed: 5 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1454 | 1454 | | |
1455 | 1455 | | |
1456 | 1456 | | |
1457 | | - | |
| 1457 | + | |
| 1458 | + | |
1458 | 1459 | | |
1459 | 1460 | | |
1460 | 1461 | | |
| |||
1501 | 1502 | | |
1502 | 1503 | | |
1503 | 1504 | | |
1504 | | - | |
| 1505 | + | |
| 1506 | + | |
| 1507 | + | |
1505 | 1508 | | |
1506 | 1509 | | |
1507 | 1510 | | |
| |||
0 commit comments