diff --git a/MacOSExample/src/App.tsx b/MacOSExample/src/App.tsx
index e928ef4893..b398ab6fc2 100644
--- a/MacOSExample/src/App.tsx
+++ b/MacOSExample/src/App.tsx
@@ -13,6 +13,7 @@ import {
import Draggable from './basic/draggable';
import PinchableBox from './recipes/scaleAndRotate';
import Tap from './basic/tap';
+import LongPressExample from './basic/longPress';
import ManualExample from './basic/manual';
interface Example {
@@ -32,6 +33,7 @@ const EXAMPLES: ExamplesSection[] = [
{ name: 'Draggable', component: Draggable },
{ name: 'Pinch & rotate', component: PinchableBox },
{ name: 'Tap', component: Tap },
+ { name: 'LongPress', component: LongPressExample },
{ name: 'Manual', component: ManualExample },
],
},
diff --git a/MacOSExample/src/basic/longPress/index.tsx b/MacOSExample/src/basic/longPress/index.tsx
new file mode 100644
index 0000000000..fcb4099fe0
--- /dev/null
+++ b/MacOSExample/src/basic/longPress/index.tsx
@@ -0,0 +1,103 @@
+import { StyleSheet, View } from 'react-native';
+import { Gesture, GestureDetector } from 'react-native-gesture-handler';
+import Animated, {
+ interpolateColor,
+ useAnimatedStyle,
+ useSharedValue,
+ withTiming,
+} from 'react-native-reanimated';
+
+const Durations = {
+ LongPress: 750,
+ Reset: 350,
+ Scale: 120,
+};
+
+const Colors = {
+ Initial: '#0a2688',
+ Loading: '#6fcef5',
+ Success: '#32a852',
+ Fail: '#b02525',
+};
+
+export default function LongPressExample() {
+ const isPressed = useSharedValue(false);
+ const colorProgress = useSharedValue(0);
+ const color1 = useSharedValue(Colors.Initial);
+ const color2 = useSharedValue(Colors.Loading);
+
+ const animatedStyles = useAnimatedStyle(() => {
+ const backgroundColor = interpolateColor(
+ colorProgress.value,
+ [0, 1],
+ [color1.value, color2.value]
+ );
+
+ return {
+ transform: [
+ {
+ scale: withTiming(isPressed.value ? 1.2 : 1, {
+ duration: Durations.Scale,
+ }),
+ },
+ ],
+ backgroundColor,
+ };
+ });
+
+ const g = Gesture.LongPress()
+ .onBegin(() => {
+ console.log('onBegin');
+
+ isPressed.value = true;
+ colorProgress.value = withTiming(1, {
+ duration: Durations.LongPress,
+ });
+ })
+ .onStart(() => console.log('onStart'))
+ .onEnd(() => console.log('onEnd'))
+ .onFinalize((_, success) => {
+ console.log('onFinalize', success);
+
+ isPressed.value = false;
+
+ color1.value = Colors.Initial;
+ color2.value = success ? Colors.Success : Colors.Fail;
+
+ colorProgress.value = withTiming(
+ 0,
+ {
+ duration: Durations.Reset,
+ },
+ () => {
+ color2.value = Colors.Loading;
+ }
+ );
+ })
+ .onTouchesDown(() => console.log('onTouchesDown'))
+ .onTouchesMove(() => console.log('onTouchesMove'))
+ .onTouchesUp(() => console.log('onTouchesUp'))
+ .minDuration(Durations.LongPress);
+
+ return (
+
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ justifyContent: 'space-around',
+ alignItems: 'center',
+ },
+
+ pressBox: {
+ width: 100,
+ height: 100,
+ borderRadius: 20,
+ },
+});
diff --git a/apple/Handlers/RNLongPressHandler.m b/apple/Handlers/RNLongPressHandler.m
index cd6e5dba9c..153fea91d7 100644
--- a/apple/Handlers/RNLongPressHandler.m
+++ b/apple/Handlers/RNLongPressHandler.m
@@ -7,22 +7,36 @@
//
#import "RNLongPressHandler.h"
+#import
#if !TARGET_OS_OSX
#import
-#import
-
@interface RNBetterLongPressGestureRecognizer : UILongPressGestureRecognizer {
+#else
+@interface RNBetterLongPressGestureRecognizer : NSGestureRecognizer {
+ dispatch_block_t block;
+#endif
+
CFTimeInterval startTime;
CFTimeInterval previousTime;
}
+#if TARGET_OS_OSX
+@property (nonatomic, assign) double minimumPressDuration;
+@property (nonatomic, assign) double allowableMovement;
+#endif
+
- (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler;
-- (void)handleGesture:(UIGestureRecognizer *)recognizer;
- (NSUInteger)getDuration;
+#if !TARGET_OS_OSX
+- (void)handleGesture:(UIGestureRecognizer *)recognizer;
+#else
+- (void)handleGesture:(NSGestureRecognizer *)recognizer;
+#endif
+
@end
@implementation RNBetterLongPressGestureRecognizer {
@@ -55,6 +69,8 @@ - (CGPoint)translationInView
return CGPointMake(currentPosition.x - _initPosition.x, currentPosition.y - _initPosition.y);
}
+#if !TARGET_OS_OSX
+
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[_gestureHandler setCurrentPointerType:event];
@@ -72,10 +88,7 @@ - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
[super touchesMoved:touches withEvent:event];
[_gestureHandler.pointerTracker touchesMoved:touches withEvent:event];
- CGPoint trans = [self translationInView];
- if ((_gestureHandler.shouldCancelWhenOutside && ![_gestureHandler containsPointInView]) ||
- (TEST_MAX_IF_NOT_NAN(
- fabs(trans.y * trans.y + trans.x * trans.x), self.allowableMovement * self.allowableMovement))) {
+ if ([self shouldCancelGesture]) {
self.enabled = NO;
self.enabled = YES;
}
@@ -94,6 +107,75 @@ - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)ev
[self reset];
}
+#else
+- (void)mouseDown:(NSEvent *)event
+{
+ self.state = NSGestureRecognizerStateBegan;
+ startTime = CACurrentMediaTime();
+
+ [_gestureHandler.pointerTracker touchesBegan:[NSSet setWithObject:event] withEvent:event];
+
+ _initPosition = [self locationInView:self.view];
+
+ __weak typeof(self) weakSelf = self;
+
+ block = dispatch_block_create(0, ^{
+ __strong typeof(self) strongSelf = weakSelf;
+
+ if (strongSelf) {
+ strongSelf.state = NSGestureRecognizerStateChanged;
+ }
+ });
+
+ dispatch_after(
+ dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.minimumPressDuration * NSEC_PER_SEC)),
+ dispatch_get_main_queue(),
+ block);
+}
+
+- (void)mouseDragged:(NSEvent *)event
+{
+ [_gestureHandler.pointerTracker touchesMoved:[NSSet setWithObject:event] withEvent:event];
+
+ if (block == nil) {
+ return;
+ }
+
+ if ([self shouldCancelGesture]) {
+ dispatch_block_cancel(block);
+ block = nil;
+
+ self.state = self.state == NSGestureRecognizerStateChanged ? NSGestureRecognizerStateCancelled
+ : NSGestureRecognizerStateFailed;
+ }
+}
+
+- (void)mouseUp:(NSEvent *)event
+{
+ [_gestureHandler.pointerTracker touchesEnded:[NSSet setWithObject:event] withEvent:event];
+
+ if (block) {
+ dispatch_block_cancel(block);
+ block = nil;
+ }
+
+ self.state =
+ self.state == NSGestureRecognizerStateChanged ? NSGestureRecognizerStateEnded : NSGestureRecognizerStateFailed;
+}
+
+#endif
+
+- (BOOL)shouldCancelGesture
+{
+ CGPoint trans = [self translationInView];
+
+ BOOL shouldBeCancelledOutside = _gestureHandler.shouldCancelWhenOutside && ![_gestureHandler containsPointInView];
+ BOOL distanceExceeded =
+ TEST_MAX_IF_NOT_NAN(fabs(trans.y * trans.y + trans.x * trans.x), self.allowableMovement * self.allowableMovement);
+
+ return shouldBeCancelledOutside || distanceExceeded;
+}
+
- (void)reset
{
if (self.state == UIGestureRecognizerStateFailed) {
@@ -126,7 +208,7 @@ - (instancetype)initWithTag:(NSNumber *)tag
- (void)resetConfig
{
[super resetConfig];
- UILongPressGestureRecognizer *recognizer = (UILongPressGestureRecognizer *)_recognizer;
+ RNBetterLongPressGestureRecognizer *recognizer = (RNBetterLongPressGestureRecognizer *)_recognizer;
recognizer.minimumPressDuration = 0.5;
recognizer.allowableMovement = 10;
@@ -135,7 +217,7 @@ - (void)resetConfig
- (void)configure:(NSDictionary *)config
{
[super configure:config];
- UILongPressGestureRecognizer *recognizer = (UILongPressGestureRecognizer *)_recognizer;
+ RNBetterLongPressGestureRecognizer *recognizer = (RNBetterLongPressGestureRecognizer *)_recognizer;
id prop = config[@"minDurationMs"];
if (prop != nil) {
@@ -148,6 +230,8 @@ - (void)configure:(NSDictionary *)config
}
}
+#if !TARGET_OS_OSX
+
- (RNGestureHandlerState)state
{
// For long press recognizer we treat "Began" state as "active"
@@ -178,21 +262,17 @@ - (RNGestureHandlerEventExtraData *)eventExtraData:(UIGestureRecognizer *)recogn
withDuration:[(RNBetterLongPressGestureRecognizer *)recognizer getDuration]
withPointerType:_pointerType];
}
-@end
#else
-@implementation RNLongPressGestureHandler
-
-- (instancetype)initWithTag:(NSNumber *)tag
+- (RNGestureHandlerEventExtraData *)eventExtraData:(NSGestureRecognizer *)recognizer
{
- RCTLogWarn(@"LongPressGestureHandler is not supported on macOS");
- if ((self = [super initWithTag:tag])) {
- _recognizer = [NSGestureRecognizer alloc];
- }
- return self;
+ return [RNGestureHandlerEventExtraData forPosition:[recognizer locationInView:recognizer.view]
+ withAbsolutePosition:[recognizer locationInView:recognizer.view.window.contentView]
+ withNumberOfTouches:1
+ withDuration:[(RNBetterLongPressGestureRecognizer *)recognizer getDuration]
+ withPointerType:RNGestureHandlerMouse];
}
-@end
-
#endif
+@end