You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

244 lines
6.1 KiB

#import "RNGestureHandlerPointerTracker.h"
#import "RNGestureHandler.h"
#import <React/UIView+React.h>
@implementation RNGestureHandlerPointerTracker {
__weak RNGestureHandler *_gestureHandler;
UITouch *_trackedPointers[MAX_POINTERS_COUNT];
int _trackedPointersCount;
}
- (id)initWithGestureHandler:(id)gestureHandler
{
_gestureHandler = gestureHandler;
_trackedPointersCount = 0;
_changedPointersData = nil;
_allPointersData = nil;
for (int i = 0; i < MAX_POINTERS_COUNT; i++) {
_trackedPointers[i] = nil;
}
return self;
}
- (int)registerTouch:(UITouch *)touch
{
for (int index = 0; index < MAX_POINTERS_COUNT; index++) {
if (_trackedPointers[index] == nil) {
_trackedPointers[index] = touch;
return index;
}
}
return -1;
}
- (int)unregisterTouch:(UITouch *)touch
{
for (int index = 0; index < MAX_POINTERS_COUNT; index++) {
if (_trackedPointers[index] == touch) {
_trackedPointers[index] = nil;
return index;
}
}
return -1;
}
- (int)findTouchIndex:(UITouch *)touch
{
for (int index = 0; index < MAX_POINTERS_COUNT; index++) {
if (_trackedPointers[index] == touch) {
return index;
}
}
return -1;
}
- (int)registeredTouchesCount
{
int count = 0;
for (int i = 0; i < MAX_POINTERS_COUNT; i++) {
if (_trackedPointers[i] != nil) {
count++;
}
}
return count;
}
- (NSDictionary *)extractPointerData:(int)index forTouch:(UITouch *)touch
{
CGPoint relativePos = [touch locationInView:_gestureHandler.recognizer.view];
CGPoint absolutePos = [touch locationInView:_gestureHandler.recognizer.view.window];
return @{
@"id" : @(index),
@"x" : @(relativePos.x),
@"y" : @(relativePos.y),
@"absoluteX" : @(absolutePos.x),
@"absoluteY" : @(absolutePos.y)
};
}
- (void)extractAllTouches
{
int registeredTouches = [self registeredTouchesCount];
NSDictionary *data[registeredTouches];
int nextIndex = 0;
for (int i = 0; i < MAX_POINTERS_COUNT; i++) {
UITouch *touch = _trackedPointers[i];
if (touch != nil) {
data[nextIndex++] = [self extractPointerData:i forTouch:touch];
}
}
_allPointersData = [[NSArray alloc] initWithObjects:data count:registeredTouches];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
if (!_gestureHandler.needsPointerData) {
return;
}
_eventType = RNGHTouchEventTypePointerDown;
NSDictionary *data[touches.count];
for (int i = 0; i < [touches count]; i++) {
UITouch *touch = [[touches allObjects] objectAtIndex:i];
int index = [self registerTouch:touch];
if (index >= 0) {
_trackedPointersCount++;
}
data[i] = [self extractPointerData:index forTouch:touch];
}
_changedPointersData = [[NSArray alloc] initWithObjects:data count:[touches count]];
// extract all touches last to include the ones that were just added
[self extractAllTouches];
[self sendEvent];
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
if (!_gestureHandler.needsPointerData) {
return;
}
_eventType = RNGHTouchEventTypePointerMove;
NSDictionary *data[touches.count];
for (int i = 0; i < [touches count]; i++) {
UITouch *touch = [[touches allObjects] objectAtIndex:i];
int index = [self findTouchIndex:touch];
data[i] = [self extractPointerData:index forTouch:touch];
}
_changedPointersData = [[NSArray alloc] initWithObjects:data count:[touches count]];
[self extractAllTouches];
[self sendEvent];
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
if (!_gestureHandler.needsPointerData) {
return;
}
// extract all touches first to include the ones that were just lifted
[self extractAllTouches];
_eventType = RNGHTouchEventTypePointerUp;
NSDictionary *data[touches.count];
for (int i = 0; i < [touches count]; i++) {
UITouch *touch = [[touches allObjects] objectAtIndex:i];
int index = [self unregisterTouch:touch];
if (index >= 0) {
_trackedPointersCount--;
}
data[i] = [self extractPointerData:index forTouch:touch];
}
_changedPointersData = [[NSArray alloc] initWithObjects:data count:[touches count]];
[self sendEvent];
}
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
if (!_gestureHandler.needsPointerData) {
return;
}
[self reset];
}
- (void)reset
{
if (!_gestureHandler.needsPointerData) {
return;
}
if (_trackedPointersCount == 0) {
// gesture has finished because all pointers were lifted, reset event type to send state change event
_eventType = RNGHTouchEventTypeUndetermined;
} else {
// turns out that the gesture may be made to fail without calling touchesCancelled in that case there
// are still tracked pointers but the recognizer state is already set to UIGestureRecognizerStateFailed
// we need to clear the pointers and send info about their cancellation
[self cancelPointers];
}
[_gestureHandler reset];
}
- (void)cancelPointers
{
// extract all touches first to include the ones that were just cancelled
[self extractAllTouches];
int registeredTouches = [self registeredTouchesCount];
if (registeredTouches > 0) {
int nextIndex = 0;
NSDictionary *data[registeredTouches];
for (int i = 0; i < MAX_POINTERS_COUNT; i++) {
UITouch *touch = _trackedPointers[i];
if (touch != nil) {
data[nextIndex++] = [self extractPointerData:i forTouch:touch];
[self unregisterTouch:touch];
}
}
_eventType = RNGHTouchEventTypeCancelled;
_changedPointersData = [[NSArray alloc] initWithObjects:data count:registeredTouches];
[self sendEvent];
_trackedPointersCount = 0;
}
}
- (void)sendEvent
{
// it may happen that the gesture recognizer is reset after it's been unbound from the view,
// it that recognizer tried to send event, the app would crash because the target of the event
// would be nil.
if (!_gestureHandler.needsPointerData || _gestureHandler.recognizer.view.reactTag == nil) {
return;
}
[_gestureHandler sendTouchEventInState:[_gestureHandler state]
forViewWithTag:_gestureHandler.recognizer.view.reactTag];
}
@end