16
16
17
17
#ifdef RCT_NEW_ARCH_ENABLED
18
18
#import < React/RCTSurfaceTouchHandler.h>
19
+ #import < React/RCTSurfaceView.h>
19
20
#import < React/RCTViewComponentView.h>
20
21
#else
21
22
#import < React/RCTTouchHandler.h>
37
38
RCTDefaultLogFunction ( \
38
39
RCTLogLevelInfo, RCTLogSourceNative, @(__FILE__), @(__LINE__), [NSString stringWithFormat:__VA_ARGS__])
39
40
41
+ constexpr int NEW_ARCH_NUMBER_OF_ATTACH_RETRIES = 25;
42
+
40
43
@interface RNGestureHandlerManager () <RNGestureHandlerEventEmitter, RNRootViewGestureRecognizerDelegate>
41
44
42
45
@end
@@ -45,6 +48,7 @@ @implementation RNGestureHandlerManager {
45
48
RNGestureHandlerRegistry *_registry;
46
49
RCTUIManager *_uiManager;
47
50
NSHashTable <RNRootViewGestureRecognizer *> *_rootViewGestureRecognizers;
51
+ NSMutableDictionary <NSNumber *, NSNumber *> *_attachRetryCounter;
48
52
RCTEventDispatcher *_eventDispatcher;
49
53
id _reanimatedModule;
50
54
}
@@ -56,6 +60,7 @@ - (instancetype)initWithUIManager:(RCTUIManager *)uiManager eventDispatcher:(RCT
56
60
_eventDispatcher = eventDispatcher;
57
61
_registry = [RNGestureHandlerRegistry new ];
58
62
_rootViewGestureRecognizers = [NSHashTable hashTableWithOptions: NSPointerFunctionsWeakMemory ];
63
+ _attachRetryCounter = [[NSMutableDictionary alloc ] init ];
59
64
_reanimatedModule = nil ;
60
65
}
61
66
return self;
@@ -100,12 +105,39 @@ - (void)attachGestureHandler:(nonnull NSNumber *)handlerTag
100
105
UIView *view = [_uiManager viewForReactTag: viewTag];
101
106
102
107
#ifdef RCT_NEW_ARCH_ENABLED
103
- if (view == nil ) {
104
- // Happens when the view with given tag has been flattened.
105
- // We cannot attach gesture handler to a non-existent view.
108
+ if (view == nil || view.superview == nil ) {
109
+ // There are a few reasons we could end up here:
110
+ // - the native view corresponding to the viewtag hasn't yet been created
111
+ // - the native view has been created, but it's not attached to window
112
+ // - the native view will not exist because it got flattened
113
+ // In the first two cases we just want to wait until the view gets created or gets attached to its superview
114
+ // In the third case we don't want to do anything but we cannot easily distinguish it here, hece the abomination
115
+ // below
116
+ // TODO: would be great to have a better solution, although it might require migration to the shadow nodes from
117
+ // viewTags
118
+
119
+ NSNumber *counter = [_attachRetryCounter objectForKey: viewTag];
120
+ if (counter == nil ) {
121
+ counter = @1 ;
122
+ } else {
123
+ counter = [NSNumber numberWithInt: counter.intValue + 1 ];
124
+ }
125
+
126
+ if (counter.intValue > NEW_ARCH_NUMBER_OF_ATTACH_RETRIES) {
127
+ [_attachRetryCounter removeObjectForKey: viewTag];
128
+ } else {
129
+ [_attachRetryCounter setObject: counter forKey: viewTag];
130
+
131
+ dispatch_after (dispatch_time (DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue (), ^{
132
+ [self attachGestureHandler: handlerTag toViewWithTag: viewTag withActionType: actionType];
133
+ });
134
+ }
135
+
106
136
return ;
107
137
}
108
138
139
+ [_attachRetryCounter removeObjectForKey: viewTag];
140
+
109
141
// I think it should be moved to RNNativeViewHandler, but that would require
110
142
// additional logic for setting contentView.reactTag, this works for now
111
143
if ([view isKindOfClass: [RCTViewComponentView class ]]) {
@@ -164,18 +196,26 @@ - (id)handlerWithTag:(NSNumber *)handlerTag
164
196
165
197
- (void )registerViewWithGestureRecognizerAttachedIfNeeded : (UIView *)childView
166
198
{
199
+ #ifdef RCT_NEW_ARCH_ENABLED
200
+ UIView *touchHandlerView = childView;
201
+
202
+ while (touchHandlerView != nil && ![touchHandlerView isKindOfClass: [RCTSurfaceView class ]]) {
203
+ touchHandlerView = touchHandlerView.superview ;
204
+ }
205
+ #else
167
206
UIView *parent = childView;
168
207
while (parent != nil && ![parent respondsToSelector: @selector (touchHandler )])
169
208
parent = parent.superview ;
170
209
171
- // Many views can return the same touchHandler so we check if the one we want to register
172
- // is not already present in the set.
173
210
UIView *touchHandlerView = [[parent performSelector: @selector (touchHandler )] view ];
211
+ #endif // RCT_NEW_ARCH_ENABLED
174
212
175
213
if (touchHandlerView == nil ) {
176
214
return ;
177
215
}
178
216
217
+ // Many views can return the same touchHandler so we check if the one we want to register
218
+ // is not already present in the set.
179
219
for (UIGestureRecognizer *recognizer in touchHandlerView.gestureRecognizers ) {
180
220
if ([recognizer isKindOfClass: [RNRootViewGestureRecognizer class ]]) {
181
221
return ;
@@ -208,10 +248,19 @@ - (void)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
208
248
return ;
209
249
210
250
#ifdef RCT_NEW_ARCH_ENABLED
211
- RCTSurfaceTouchHandler *touchHandler = [viewWithTouchHandler performSelector: @selector (touchHandler )];
251
+ UIGestureRecognizer *touchHandler = nil ;
252
+
253
+ // touchHandler (RCTSurfaceTouchHandler) is private in RCTFabricSurface so we have to do
254
+ // this little trick to get access to it
255
+ for (UIGestureRecognizer *recognizer in [viewWithTouchHandler gestureRecognizers ]) {
256
+ if ([recognizer isKindOfClass: [RCTSurfaceTouchHandler class ]]) {
257
+ touchHandler = recognizer;
258
+ break ;
259
+ }
260
+ }
212
261
#else
213
262
RCTTouchHandler *touchHandler = [viewWithTouchHandler performSelector: @selector (touchHandler )];
214
- #endif
263
+ #endif // RCT_NEW_ARCH_ENABLED
215
264
[touchHandler setEnabled: NO ];
216
265
[touchHandler setEnabled: YES ];
217
266
}
0 commit comments