Skip to content

Commit

Permalink
Add duration to long press event (software-mansion#1505)
Browse files Browse the repository at this point in the history
Description

Added duration property to the LongPressGestureHandler events. This change allows checking how long the entire event lasted.

Test code

```js
import React, { Component } from 'react';
import { StyleSheet, View } from 'react-native';

import {
  LongPressGestureHandler,
  LongPressGestureHandlerStateChangeEvent,
} from 'react-native-gesture-handler';

export class PressBox extends Component {
  private onHandlerStateChange = (
    event: LongPressGestureHandlerStateChangeEvent
  ) => {
    console.log(`Duration: ${event.nativeEvent.duration}`);
  };

  render() {
    return (
      <LongPressGestureHandler
        onHandlerStateChange={this.onHandlerStateChange}
        minDurationMs={600}>
          <View style={styles.box} />
      </LongPressGestureHandler>
    );
  }
}

export default class Example extends Component {
  render() {
    return (
      <PressBox />
    );
  }
}

const styles = StyleSheet.create({
  box: {
    width: 150,
    height: 150,
    alignSelf: 'center',
    backgroundColor: 'plum',
    margin: 10,
    zIndex: 200,
  },
});
```
  • Loading branch information
j-piasecki authored Jul 16, 2021
1 parent b172a79 commit 777fab8
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import android.content.Context;
import android.os.Handler;
import android.os.SystemClock;
import android.view.MotionEvent;

public class LongPressGestureHandler extends GestureHandler<LongPressGestureHandler> {
Expand All @@ -13,6 +14,7 @@ public class LongPressGestureHandler extends GestureHandler<LongPressGestureHand
private final float mDefaultMaxDistSq;
private float mMaxDistSq;
private float mStartX, mStartY;
private long mStartTime, mPreviousTime;
private Handler mHandler;

public LongPressGestureHandler(Context context) {
Expand Down Expand Up @@ -40,6 +42,7 @@ public LongPressGestureHandler setMaxDist(float maxDist) {
@Override
protected void onHandle(MotionEvent event) {
if (getState() == STATE_UNDETERMINED) {
mStartTime = mPreviousTime = SystemClock.uptimeMillis();
begin();
mStartX = event.getRawX();
mStartY = event.getRawY();
Expand Down Expand Up @@ -87,4 +90,20 @@ protected void onStateChange(int newState, int previousState) {
mHandler = null;
}
}

@Override
void dispatchStateChange(int newState, int prevState) {
mPreviousTime = SystemClock.uptimeMillis();
super.dispatchStateChange(newState, prevState);
}

@Override
void dispatchTouchEvent(MotionEvent event) {
mPreviousTime = SystemClock.uptimeMillis();
super.dispatchTouchEvent(event);
}

public int getDuration() {
return (int)(mPreviousTime - mStartTime);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import com.facebook.react.bridge.ReadableType;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.common.SystemClock;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.NativeViewHierarchyManager;
import com.facebook.react.uimanager.PixelUtil;
Expand Down Expand Up @@ -234,6 +235,7 @@ public void extractEventData(LongPressGestureHandler handler, WritableMap eventD
eventData.putDouble("y", PixelUtil.toDIPFromPixel(handler.getLastRelativePositionY()));
eventData.putDouble("absoluteX", PixelUtil.toDIPFromPixel(handler.getLastAbsolutePositionX()));
eventData.putDouble("absoluteY", PixelUtil.toDIPFromPixel(handler.getLastAbsolutePositionY()));
eventData.putInt("duration", handler.getDuration());
}
}

Expand Down
3 changes: 3 additions & 0 deletions docs/docs/api/gesture-handlers/longpress-gh.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ X coordinate, expressed in points, of the current position of the pointer (finge

Y coordinate, expressed in points, of the current position of the pointer (finger or a leading pointer when there are multiple fingers placed) relative to the root view. It is recommended to use `absoluteY` instead of [`y`](#y) in cases when the view attached to the handler can be transformed as an effect of the gesture.

### `duration`

Duration of the long press (time since the start of the event), expressed in milliseconds.
## Example

See the [multitap example](https://github.com/software-mansion/react-native-gesture-handler/blob/master/examples/Example/src/multitap/index.tsx) from [GestureHandler Example App](example.md) or view it directly on your phone by visiting [our expo demo](https://snack.expo.io/@adamgrzybowski/react-native-gesture-handler-demo).
Expand Down
38 changes: 31 additions & 7 deletions examples/Example/src/multitap/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { Component } from 'react';
import { StyleSheet, View, Alert } from 'react-native';
import { StyleSheet, View, Alert, Text } from 'react-native';

import {
LongPressGestureHandler,
Expand All @@ -12,14 +12,19 @@ import {

import { LoremIpsum } from '../common';

export class PressBox extends Component {
interface PressBoxProps {
setDuration: (duration: number) => void;
}

interface ExampleState {
longPressDuration: number;
}
export class PressBox extends Component<PressBoxProps> {
private doubleTapRef = React.createRef<TapGestureHandler>();
private onHandlerStateChange = (
event: LongPressGestureHandlerStateChangeEvent
) => {
if (event.nativeEvent.state === State.ACTIVE) {
Alert.alert("I'm being pressed for so long");
}
this.props.setDuration(event.nativeEvent.duration);
};
private onSingleTap = (event: TapGestureHandlerStateChangeEvent) => {
if (event.nativeEvent.state === State.ACTIVE) {
Expand Down Expand Up @@ -51,12 +56,28 @@ export class PressBox extends Component {
}
}

export default class Example extends Component {
export default class Example extends Component<
Record<string, never>,
ExampleState
> {
constructor(props: Record<string, never>) {
super(props);

this.state = { longPressDuration: 0 };
}

render() {
return (
<ScrollView style={styles.scrollView}>
<LoremIpsum words={40} />
<PressBox />
<Text style={styles.text}>
Duration of the last long press: {this.state.longPressDuration}ms
</Text>
<PressBox
setDuration={(duration: number) =>
this.setState({ longPressDuration: duration })
}
/>
<LoremIpsum />
</ScrollView>
);
Expand All @@ -75,4 +96,7 @@ const styles = StyleSheet.create({
margin: 10,
zIndex: 200,
},
text: {
marginLeft: 20,
},
});
35 changes: 33 additions & 2 deletions ios/Handlers/RNLongPressHandler.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,16 @@

#import <React/RCTConvert.h>

@interface RNBetterLongPressGestureRecognizer : UILongPressGestureRecognizer
#import <mach/mach_time.h>

@interface RNBetterLongPressGestureRecognizer : UILongPressGestureRecognizer {
uint64_t startTime;
uint64_t previousTime;
}

- (id)initWithGestureHandler:(RNGestureHandler*)gestureHandler;
- (void)handleGesture:(UIGestureRecognizer *)recognizer;
- (NSUInteger) getDuration;

@end

Expand All @@ -24,12 +31,22 @@ @implementation RNBetterLongPressGestureRecognizer {

- (id)initWithGestureHandler:(RNGestureHandler*)gestureHandler
{
if ((self = [super initWithTarget:gestureHandler action:@selector(handleGesture:)])) {
if ((self = [super initWithTarget:self action:@selector(handleGesture:)])) {
_gestureHandler = gestureHandler;
}
return self;
}

- (void)handleGesture:(UIGestureRecognizer *)recognizer
{
if (recognizer.state == UIGestureRecognizerStateBegan) {
startTime = mach_absolute_time();
}
previousTime = mach_absolute_time();

[_gestureHandler handleGesture:recognizer];
}

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
Expand All @@ -39,6 +56,11 @@ - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
}
}

- (NSUInteger)getDuration
{
return (NSUInteger)(((previousTime - startTime) / 1000000 + self.minimumPressDuration * 1000));
}

@end


Expand Down Expand Up @@ -88,5 +110,14 @@ - (RNGestureHandlerState)state
}
return [super state];
}

- (RNGestureHandlerEventExtraData *)eventExtraData:(UIGestureRecognizer *)recognizer
{
return [RNGestureHandlerEventExtraData
forPosition:[recognizer locationInView:recognizer.view]
withAbsolutePosition:[recognizer locationInView:recognizer.view.window]
withNumberOfTouches:recognizer.numberOfTouches
withDuration:[(RNBetterLongPressGestureRecognizer*)recognizer getDuration]];
}
@end

4 changes: 4 additions & 0 deletions ios/RNGestureHandlerEvents.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
+ (RNGestureHandlerEventExtraData *)forPosition:(CGPoint)position
withAbsolutePosition:(CGPoint)absolutePosition
withNumberOfTouches:(NSUInteger)numberOfTouches;
+ (RNGestureHandlerEventExtraData *)forPosition:(CGPoint)position
withAbsolutePosition:(CGPoint)absolutePosition
withNumberOfTouches:(NSUInteger)numberOfTouches
withDuration:(NSUInteger)duration;
+ (RNGestureHandlerEventExtraData *)forPan:(CGPoint)position
withAbsolutePosition:(CGPoint)absolutePosition
withTranslation:(CGPoint)translation
Expand Down
16 changes: 16 additions & 0 deletions ios/RNGestureHandlerEvents.m
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,22 @@ + (RNGestureHandlerEventExtraData *)forPosition:(CGPoint)position
@"numberOfPointers": @(numberOfTouches)}];
}

+ (RNGestureHandlerEventExtraData *)forPosition:(CGPoint)position
withAbsolutePosition:(CGPoint)absolutePosition
withNumberOfTouches:(NSUInteger)numberOfTouches
withDuration:(NSUInteger)duration
{
return [[RNGestureHandlerEventExtraData alloc]
initWithData:@{
@"x": @(position.x),
@"y": @(position.y),
@"absoluteX": @(absolutePosition.x),
@"absoluteY": @(absolutePosition.y),
@"numberOfPointers": @(numberOfTouches),
@"duration":@(duration)
}];
}

+ (RNGestureHandlerEventExtraData *)forPan:(CGPoint)position
withAbsolutePosition:(CGPoint)absolutePosition
withTranslation:(CGPoint)translation
Expand Down
1 change: 1 addition & 0 deletions src/handlers/gestureHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ export type LongPressGestureHandlerEventPayload = {
y: number;
absoluteX: number;
absoluteY: number;
duration: number;
};

export interface LongPressGestureHandlerProps
Expand Down

0 comments on commit 777fab8

Please sign in to comment.