Closed boclar closed 1 month ago
Hi @boclar! Thanks for reporting this problem! The thing is, it works on other platforms because we do not perform any action when you add another pointer. iOS
implementation of Gesture Handler uses native recognizers. As you can see in the docs, UILongPressGestureRecognizer
has property called numberOfTouchesRequired, which defaults to 1
.
I think the best solution would be to add numberOfPointers
property to LongPress
gesture (like in Fling).
I'll get back to you with a PR!
That makes sense. I think biggest issue is that the option for defining the number of touches does not exist for LongPress
Hi @boclar! Could you please check if this PR meets your requirements? 😅
Hello @m-bert, from what I can read from the PR description, it seems to fit my requirements. Is there anything else I can do for you?
Thank you!
I'd be glad if you could test these changes and confirm that they work
Sure, no problem.
I currently have the plugin installed, but wonder if there is a way to pull these PR updates to my branch instead of the production ones?
@m-bert I managed to have it installed in my project, plugin is in a separate folder within my root project, and I changed all the imports from node_modules to this other folder instead, which includes your PR changes.
I tested it on 2 devices and I am getting the same issue you see in the terminal logs, which stop my app from loading, so it stays on splash screen.
Error: WARN Tried to call timer with ID 9 but no such timer exists. WARN Tried to call timer with ID 9 but no such timer exists.
Here is my component code:
import { CustomGestureDetectorProps } from './custom-gesture-detector.types';
import { customGestureDetectorStyles } from './custom-gesture-detector.styles';
import { router, usePathname } from 'expo-router';
import {
Gesture,
GestureDetector,
GestureHandlerRootView,
GestureTouchEvent,
} from '../../../react-native-gesture-handler';
import { useEffect, useState } from 'react';
import { Alert, AlertProps } from '@boclar/booking-app-components';
import { useTranslation } from 'react-i18next';
import { getFromAsyncStorage } from '@/utils/storage/storage.utils';
import { View, Alert as AlertRN } from 'react-native';
import { useAuth } from '@/hooks/use-auth/use-auth.hooks';
/**
* Detects double tap gesture and navigates to report issue screen
*/
const CustomGestureDetector = ({
children,
...props
}: CustomGestureDetectorProps) => {
const styles = customGestureDetectorStyles();
const [alertMsg, setAlertMsg] = useState<{
message: string;
type: AlertProps['type'];
}>();
const { t } = useTranslation();
const pathname = usePathname();
const { isLogged } = useAuth();
// Navigate to report issue screen on double tap
// const doubleTap = Gesture.Tap()
// .runOnJS(true)
// .numberOfTaps(3)
// .onStart(() => {
// router.push('/report-issue');
// })
// .withTestId('double-tap');
const longPress = Gesture.LongPress()
.runOnJS(true)
// .minDuration(2000)
.numberOfPointers(2)
.onStart(() => {
AlertRN.alert('Long press started');
})
.onTouchesCancelled((event: GestureTouchEvent) => {
console.warn('Touches cancelled', event?.numberOfTouches);
});
useEffect(() => {
const showAlert = async () => {
const hasOpenedReportIssue = await getFromAsyncStorage(
'hasOpenedReportIssue'
);
!hasOpenedReportIssue &&
setAlertMsg({
message: t('reportIssueScreen.alertMessage'),
type: 'info',
});
};
showAlert();
}, [t]);
useEffect(() => {
// Clear alert message when navigating to report issue screen
pathname === '/report-issue' && setAlertMsg(undefined);
}, [pathname]);
return isLogged ? (
<>
{alertMsg && (
<Alert
autoDismiss={10000}
cancelable={false}
message={alertMsg.message}
onAlertClose={setAlertMsg}
position="top"
presentationStyle="absolute"
style={styles.alert}
type={alertMsg?.type}
/>
)}
<GestureHandlerRootView
style={styles.fitScreen}
{...props}
>
<GestureDetector gesture={longPress}>
<View style={styles.fitScreen}>{children}</View>
</GestureDetector>
</GestureHandlerRootView>
</>
) : (
<>{children}</>
);
};
export { CustomGestureDetector };
Tested on:
iPhone 14 Pro Max. Android Samsung S20 FE
I currently have the plugin installed, but wonder if there is a way to pull these PR updates to my branch instead of the production ones?
You can install Gesture Handler from my branch, this way you don't have to do any gymnastics with imports.
I tested it on 2 devices and I am getting the same issue you see in the terminal logs, which stop my app from loading, so it >stays on splash screen.
Error: WARN Tried to call timer with ID 9 but no such timer exists.
That's strange. Do you have anything else, like stack trace for this error?
There is no stack error, that is the only thing that shows up on the terminal as an error. I am willing to jump into a meeting if that works for you.
Unfortunately I don't have much time right now to schedule a meeting 😞
I'll try to look into it and I'll get back to you if I find something. In the meantime, would it be possible for you to create a repository with reproduction? Since it works ok on my end it is hard to determine what actually causes this issue.
Okay, I've found out that on android
code that is responsible for scheduling LongPress activation was called on every move event. I've already fixed it.
I'm not sure if that was the cause of the warning that you've got. Also, I can't see what could cause it on iOS
. Could you check if this issue is gone?
Here is the stack error. Hopefully it helps you debug the issue.
It is still not working on my end, but if it works for you, I would think that is fine. I could try again after you publish the version to npm.
ERROR Error: [Reanimated] Another instance of Reanimated was detected.
See https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#another-instance-of-reanimated-was-detected
for more details. Previous: 3.12.0, current: 3.10.1., js engine: hermes
at ContextNavigator (http://192.168.1.95:8081/src/index.ts.bundle//&platform=android&dev=true&hot=false&lazy=true&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=.%2Fsrc%2Fapp:231591:24)
at ExpoRoot (http://192.168.1.95:8081/src/index.ts.bundle//&platform=android&dev=true&hot=false&lazy=true&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=.%2Fsrc%2Fapp:231547:28)
at App
at ErrorToastContainer (http://192.168.1.95:8081/src/index.ts.bundle//&platform=android&dev=true&hot=false&lazy=true&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=.%2Fsrc%2Fapp:723792:24)
at ErrorOverlay
at withDevTools(ErrorOverlay) (http://192.168.1.95:8081/src/index.ts.bundle//&platform=android&dev=true&hot=false&lazy=true&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=.%2Fsrc%2Fapp:593260:27)
at RCTView
at View (http://192.168.1.95:8081/src/index.ts.bundle//&platform=android&dev=true&hot=false&lazy=true&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=.%2Fsrc%2Fapp:41375:43)
at RCTView
at View (http://192.168.1.95:8081/src/index.ts.bundle//&platform=android&dev=true&hot=false&lazy=true&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=.%2Fsrc%2Fapp:41375:43)
at AppContainer (http://192.168.1.95:8081/src/index.ts.bundle//&platform=android&dev=true&hot=false&lazy=true&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=.%2Fsrc%2Fapp:41218:25)
at main(RootComponent) (http://192.168.1.95:8081/src/index.ts.bundle//&platform=android&dev=true&hot=false&lazy=true&transform.engine=hermes&transform.bytecode=true&transform.routerRoot=.%2Fsrc%2Fapp:120270:28)
WARN Tried to call timer with ID 23 but no such timer exists.
I think the issue is because I cant install the compatible packages with expo install, because it is a branch, not a package, right now. Therefore it gives me the error about several instances
Oh, you're right, I forgot that expo requires specific versions (at least for native code, and in my PR there are native changes).
I've removed closes
directive from my PR - we will merge it and keep this issue open until you'd be able to confirm that it works (or not).
HI @boclar! Have you been able to test it yet? Fixing PR has already been released and I'd like to know if it works 😅
I will test it tonight
I am currently testing version "2.19.0" which is the latest version published until now. I tried on expo GO and it is giving me errors, therefore I decided to build an ios app and will keep you posted of what results I get on iPhone 14 Pro Max.
Long press not working on Expo GO using SDK 51, neither on iPhone 15. You will find logs below.
Do you think this is Expo specific error?
`[GESTURE HANDLER] Initialize gesture handler for view <RCTRootContentView: 0x106e51870; frame = (0 0; 393 852); gestureRecognizers = <NSArray: 0x600000cdfea0>; layer = <CALayer: 0x600000294240>> reactTag: 11; frame = {{0, 0}, {393, 852}}; layer = <CALayer: 0x600000294240> Detected the current OS's ImageIO PNG Decoder is buggy on indexed color PNG. Perform workaround solution... Bridge call to: deviceContexts libc++abi: terminating due to uncaught exception of type facebook::jsi::JSError: Object is not a function
TypeError: Object is not a function at anonymous (JavaScript:1:67) at apply (native) at runWorklet (JavaScript:1:202) at anonymous (JavaScript:1:701) at handleAndFlushAnimationFrame (JavaScript:1:148)`
Here is my code for gesture handling:
const CustomGestureDetector = ({
children,
...props
}: CustomGestureDetectorProps) => {
const styles = customGestureDetectorStyles();
const [alertMsg, setAlertMsg] = useState<{
message: string;
type: AlertProps['type'];
}>();
const { t } = useTranslation();
const pathname = usePathname();
// Navigate to report issue screen on double tap
const tapGesture = Gesture.Tap()
.runOnJS(true)
.numberOfTaps(3)
.onStart(() => {
router.navigate('/report-issue');
})
.withTestId('double-tap');
const longPress = Gesture.LongPress()
.numberOfPointers(1)
// .minDuration(1000)
.onStart(() => {
router.navigate('/report-issue');
console.log('long press');
})
.onTouchesCancelled(() => {
console.log('long press cancelled');
});
useEffect(() => {
const showAlert = async () => {
const hasOpenedReportIssue = await getFromAsyncStorage(
'hasOpenedReportIssue'
);
!hasOpenedReportIssue &&
setAlertMsg({
message: t('reportIssueScreen.alertMessage'),
type: 'info',
});
};
showAlert();
}, [t]);
useEffect(() => {
// Clear alert message when navigating to report issue screen
pathname === '/report-issue' && setAlertMsg(undefined);
}, [pathname]);
return (
<>
{alertMsg && (
<Alert
autoDismiss={5000}
cancelable={false}
message={alertMsg.message}
onAlertClose={setAlertMsg}
position="top"
presentationStyle="absolute"
style={styles.alert}
type={alertMsg?.type}
/>
)}
<GestureHandlerRootView
style={styles.fitScreen}
{...props}
>
<GestureDetector gesture={longPress}>
<View style={styles.fitScreen}>{children}</View>
</GestureDetector>
</GestureHandlerRootView>
</>
);
};
export { CustomGestureDetector }
I just noticed I did not have runOnJS(true) to be executed.
These were my results without runOnJS(true): When numberOfPointers is 1 app closes automatically, when greater than 1 touches still get cancelled and nothing happens.
Having runOnJS(true) executed on long press:
Press with one fingers seems to be working fine, however, numberOfPointers are ignored, regardless I define 2, 3, 4 fingers it will only detect long press with one finger.
iOS
Additionally, I have my expo app to work for web, android and ios. Now when long press is enabled as a gesture. It is not detected on the web either
Expo GO won't work since it is compatible with exact version of Gesture Handler (I'm not sure which one is used in latest expo go, but I'm pretty sure it is below 2.19).
Now when long press is enabled as a gesture. It is not detected on the web either
I've just tested numberOfPointers
on web and it works fine.
Could you prepare a reproduction that we can run? I'm not sure what's happening since all of the things you've mentioned should work. Also, code that you've posted is not copy-pastable as it contains some functions that are not defined within snippet.
@m-bert I'll have a demo for you in the next hours.
@m-bert Hey, this is the best I can get for you. Let me know if this works. You can run project with Expo Go.
[example]
Hi @boclar!
I've looked into your package.json
and here's what I've found:
"react-native-gesture-handler": "2.16.1",
so there's no way it will work since PR with numberOfPointers
was released in 2.19.0
. The other thing is I'm not sure which version is supported by Expo GO (even if you install newer version of package, only the JS side will be affected).
Also this is quite complex example, not MCVE.
Given number of errors that you get (for example the one with Reanimated) we believe that the problem lies in your complex setup, not in the Gesture Handler itself. Unfortunately at this point we don't have capacity to help you.
Description
For some reason long press with one finger is working like a charm, but it does not work when screen is pressed with more than 1 fingers on iOS specifically.
Steps to reproduce
Snack or a link to a repository
N/A
Gesture Handler version
~2.16.1
React Native version
0.74.3
Platforms
iOS
JavaScript runtime
None
Workflow
None
Architecture
None
Build type
None
Device
None
Device model
iPhone 14 Pro Max
Acknowledgements
Yes