1#import "RNGestureHandlerPointerTracker.h" 2#import "RNGestureHandler.h" 3 4#import <React/UIView+React.h> 5 6@implementation RNGestureHandlerPointerTracker { 7 __weak RNGestureHandler *_gestureHandler; 8 UITouch *_trackedPointers[MAX_POINTERS_COUNT]; 9 int _trackedPointersCount; 10} 11 12- (id)initWithGestureHandler:(id)gestureHandler 13{ 14 _gestureHandler = gestureHandler; 15 _trackedPointersCount = 0; 16 _changedPointersData = nil; 17 _allPointersData = nil; 18 19 for (int i = 0; i < MAX_POINTERS_COUNT; i++) { 20 _trackedPointers[i] = nil; 21 } 22 23 return self; 24} 25 26- (int)registerTouch:(UITouch *)touch 27{ 28 for (int index = 0; index < MAX_POINTERS_COUNT; index++) { 29 if (_trackedPointers[index] == nil) { 30 _trackedPointers[index] = touch; 31 return index; 32 } 33 } 34 35 return -1; 36} 37 38- (int)unregisterTouch:(UITouch *)touch 39{ 40 for (int index = 0; index < MAX_POINTERS_COUNT; index++) { 41 if (_trackedPointers[index] == touch) { 42 _trackedPointers[index] = nil; 43 return index; 44 } 45 } 46 47 return -1; 48} 49 50- (int)findTouchIndex:(UITouch *)touch 51{ 52 for (int index = 0; index < MAX_POINTERS_COUNT; index++) { 53 if (_trackedPointers[index] == touch) { 54 return index; 55 } 56 } 57 return -1; 58} 59 60- (int)registeredTouchesCount 61{ 62 int count = 0; 63 for (int i = 0; i < MAX_POINTERS_COUNT; i++) { 64 if (_trackedPointers[i] != nil) { 65 count++; 66 } 67 } 68 return count; 69} 70 71- (NSDictionary *)extractPointerData:(int)index forTouch:(UITouch *)touch 72{ 73 CGPoint relativePos = [touch locationInView:_gestureHandler.recognizer.view]; 74 CGPoint absolutePos = [touch locationInView:_gestureHandler.recognizer.view.window]; 75 76 return @{ 77 @"id" : @(index), 78 @"x" : @(relativePos.x), 79 @"y" : @(relativePos.y), 80 @"absoluteX" : @(absolutePos.x), 81 @"absoluteY" : @(absolutePos.y) 82 }; 83} 84 85- (void)extractAllTouches 86{ 87 int registeredTouches = [self registeredTouchesCount]; 88 89 NSDictionary *data[registeredTouches]; 90 int nextIndex = 0; 91 92 for (int i = 0; i < MAX_POINTERS_COUNT; i++) { 93 UITouch *touch = _trackedPointers[i]; 94 if (touch != nil) { 95 data[nextIndex++] = [self extractPointerData:i forTouch:touch]; 96 } 97 } 98 99 _allPointersData = [[NSArray alloc] initWithObjects:data count:registeredTouches]; 100} 101 102- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event 103{ 104 if (!_gestureHandler.needsPointerData) { 105 return; 106 } 107 108 _eventType = RNGHTouchEventTypePointerDown; 109 110 NSDictionary *data[touches.count]; 111 112 for (int i = 0; i < [touches count]; i++) { 113 UITouch *touch = [[touches allObjects] objectAtIndex:i]; 114 int index = [self registerTouch:touch]; 115 if (index >= 0) { 116 _trackedPointersCount++; 117 } 118 119 data[i] = [self extractPointerData:index forTouch:touch]; 120 } 121 122 _changedPointersData = [[NSArray alloc] initWithObjects:data count:[touches count]]; 123 // extract all touches last to include the ones that were just added 124 [self extractAllTouches]; 125 [self sendEvent]; 126} 127 128- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event 129{ 130 if (!_gestureHandler.needsPointerData) { 131 return; 132 } 133 134 _eventType = RNGHTouchEventTypePointerMove; 135 136 NSDictionary *data[touches.count]; 137 138 for (int i = 0; i < [touches count]; i++) { 139 UITouch *touch = [[touches allObjects] objectAtIndex:i]; 140 int index = [self findTouchIndex:touch]; 141 data[i] = [self extractPointerData:index forTouch:touch]; 142 } 143 144 _changedPointersData = [[NSArray alloc] initWithObjects:data count:[touches count]]; 145 [self extractAllTouches]; 146 [self sendEvent]; 147} 148 149- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event 150{ 151 if (!_gestureHandler.needsPointerData) { 152 return; 153 } 154 155 // extract all touches first to include the ones that were just lifted 156 [self extractAllTouches]; 157 158 _eventType = RNGHTouchEventTypePointerUp; 159 160 NSDictionary *data[touches.count]; 161 162 for (int i = 0; i < [touches count]; i++) { 163 UITouch *touch = [[touches allObjects] objectAtIndex:i]; 164 int index = [self unregisterTouch:touch]; 165 if (index >= 0) { 166 _trackedPointersCount--; 167 } 168 169 data[i] = [self extractPointerData:index forTouch:touch]; 170 } 171 172 _changedPointersData = [[NSArray alloc] initWithObjects:data count:[touches count]]; 173 [self sendEvent]; 174} 175 176- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event 177{ 178 if (!_gestureHandler.needsPointerData) { 179 return; 180 } 181 182 [self reset]; 183} 184 185- (void)reset 186{ 187 if (!_gestureHandler.needsPointerData) { 188 return; 189 } 190 191 if (_trackedPointersCount == 0) { 192 // gesture has finished because all pointers were lifted, reset event type to send state change event 193 _eventType = RNGHTouchEventTypeUndetermined; 194 } else { 195 // turns out that the gesture may be made to fail without calling touchesCancelled in that case there 196 // are still tracked pointers but the recognizer state is already set to UIGestureRecognizerStateFailed 197 // we need to clear the pointers and send info about their cancellation 198 [self cancelPointers]; 199 } 200 201 [_gestureHandler reset]; 202} 203 204- (void)cancelPointers 205{ 206 // extract all touches first to include the ones that were just cancelled 207 [self extractAllTouches]; 208 209 int registeredTouches = [self registeredTouchesCount]; 210 211 if (registeredTouches > 0) { 212 int nextIndex = 0; 213 NSDictionary *data[registeredTouches]; 214 215 for (int i = 0; i < MAX_POINTERS_COUNT; i++) { 216 UITouch *touch = _trackedPointers[i]; 217 if (touch != nil) { 218 data[nextIndex++] = [self extractPointerData:i forTouch:touch]; 219 [self unregisterTouch:touch]; 220 } 221 } 222 223 _eventType = RNGHTouchEventTypeCancelled; 224 _changedPointersData = [[NSArray alloc] initWithObjects:data count:registeredTouches]; 225 [self sendEvent]; 226 _trackedPointersCount = 0; 227 } 228} 229 230- (void)sendEvent 231{ 232 // it may happen that the gesture recognizer is reset after it's been unbound from the view, 233 // it that recognizer tried to send event, the app would crash because the target of the event 234 // would be nil. 235 if (!_gestureHandler.needsPointerData || _gestureHandler.recognizer.view.reactTag == nil) { 236 return; 237 } 238 239 [_gestureHandler sendTouchEventInState:[_gestureHandler state] 240 forViewWithTag:_gestureHandler.recognizer.view.reactTag]; 241} 242 243@end 244