1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 | #import "RCTUIManager.h"
|
9 |
|
10 | #import <AVFoundation/AVFoundation.h>
|
11 |
|
12 | #import <yoga/Yoga.h> // TODO(macOS ISS#2323203)
|
13 |
|
14 | #import "RCTAssert.h"
|
15 | #import "RCTBridge+Private.h"
|
16 | #import "RCTBridge.h"
|
17 | #import "RCTComponent.h"
|
18 | #import "RCTComponentData.h"
|
19 | #import "RCTConvert.h"
|
20 | #import "RCTDefines.h"
|
21 | #import "RCTDevSettings.h" // TODO(macOS ISS#2323203)
|
22 | #import "RCTEventDispatcher.h"
|
23 | #import "RCTLayoutAnimation.h"
|
24 | #import "RCTLayoutAnimationGroup.h"
|
25 | #import "RCTLog.h"
|
26 | #import "RCTModuleData.h"
|
27 | #import "RCTModuleMethod.h"
|
28 | #import "RCTProfile.h"
|
29 | #import "RCTRootContentView.h"
|
30 | #import "RCTRootShadowView.h"
|
31 | #import "RCTRootViewInternal.h"
|
32 | #if !TARGET_OS_OSX // TODO(macOS ISS#2323203)
|
33 | #import "RCTScrollableProtocol.h"
|
34 | #endif // TODO(macOS ISS#2323203)
|
35 | #import "RCTShadowView+Internal.h"
|
36 | #import "RCTShadowView.h"
|
37 | #import "RCTSurfaceRootShadowView.h"
|
38 | #import "RCTSurfaceRootView.h"
|
39 | #import "RCTUIManagerObserverCoordinator.h"
|
40 | #import "RCTUIManagerUtils.h"
|
41 | #import "RCTUtils.h"
|
42 | #import "RCTView.h"
|
43 | #import "RCTViewManager.h"
|
44 | #import "UIView+React.h"
|
45 | #import "RCTDeviceInfo.h" // TODO(macOS ISS#2323203)
|
46 |
|
47 | #import <React/RCTUIKit.h>
|
48 |
|
49 | void RCTTraverseViewNodes(id<RCTComponent> view, void (^block)(id<RCTComponent>)) // TODO(OSS Candidate ISS#2710739)
|
50 | {
|
51 | if (view.reactTag) {
|
52 | block(view);
|
53 |
|
54 | for (id<RCTComponent> subview in view.reactSubviews) {
|
55 | RCTTraverseViewNodes(subview, block);
|
56 | }
|
57 | }
|
58 | }
|
59 |
|
60 | static NSString *RCTNativeIDRegistryKey(NSString *nativeID, NSNumber *rootTag)
|
61 | {
|
62 | if (!nativeID || !rootTag) {
|
63 | return @"";
|
64 | }
|
65 | return [NSString stringWithFormat:@"%@-%@", rootTag, nativeID];
|
66 | }
|
67 |
|
68 | NSString *const RCTUIManagerWillUpdateViewsDueToContentSizeMultiplierChangeNotification =
|
69 | @"RCTUIManagerWillUpdateViewsDueToContentSizeMultiplierChangeNotification";
|
70 |
|
71 | @implementation RCTUIManager {
|
72 | // Root views are only mutated on the shadow queue
|
73 | NSMutableSet<NSNumber *> *_rootViewTags;
|
74 | NSMutableArray<RCTViewManagerUIBlock> *_pendingUIBlocks;
|
75 |
|
76 | // Animation
|
77 | RCTLayoutAnimationGroup *_layoutAnimationGroup; // Main thread only
|
78 |
|
79 | NSMutableDictionary<NSNumber *, RCTShadowView *> *_shadowViewRegistry; // RCT thread only
|
80 | NSMutableDictionary<NSNumber *, RCTPlatformView *> *_viewRegistry; // Main thread only // TODO(macOS ISS#2323203)
|
81 | NSMapTable<NSString *, RCTPlatformView *> *_nativeIDRegistry; // TODO(macOS ISS#2323203)
|
82 |
|
83 | NSMapTable<RCTShadowView *, NSArray<NSString *> *> *_shadowViewsWithUpdatedProps; // UIManager queue only.
|
84 | NSHashTable<RCTShadowView *> *_shadowViewsWithUpdatedChildren; // UIManager queue only.
|
85 |
|
86 | // Keyed by viewName
|
87 | NSMutableDictionary *_componentDataByName;
|
88 | }
|
89 |
|
90 | @synthesize bridge = _bridge;
|
91 |
|
92 | RCT_EXPORT_MODULE()
|
93 |
|
94 | + (BOOL)requiresMainQueueSetup
|
95 | {
|
96 | return NO;
|
97 | }
|
98 |
|
99 | - (void)invalidate
|
100 | {
|
101 | |
102 |
|
103 |
|
104 |
|
105 | // This only accessed from the shadow queue
|
106 | _pendingUIBlocks = nil;
|
107 |
|
108 | RCTExecuteOnMainQueue(^{
|
109 | RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"UIManager invalidate", nil);
|
110 | for (NSNumber *rootViewTag in self->_rootViewTags) {
|
111 | RCTUIView *rootView = self->_viewRegistry[rootViewTag]; // TODO(macOS ISS#3536887)
|
112 | if ([rootView conformsToProtocol:@protocol(RCTInvalidating)]) {
|
113 | [(id<RCTInvalidating>)rootView invalidate];
|
114 | }
|
115 | }
|
116 |
|
117 | self->_rootViewTags = nil;
|
118 | self->_shadowViewRegistry = nil;
|
119 | self->_viewRegistry = nil;
|
120 | self->_nativeIDRegistry = nil;
|
121 | self->_bridge = nil;
|
122 |
|
123 | [[NSNotificationCenter defaultCenter] removeObserver:self];
|
124 | RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
|
125 | });
|
126 | }
|
127 |
|
128 | - (NSMutableDictionary<NSNumber *, RCTShadowView *> *)shadowViewRegistry
|
129 | {
|
130 | // NOTE: this method only exists so that it can be accessed by unit tests
|
131 | if (!_shadowViewRegistry) {
|
132 | _shadowViewRegistry = [NSMutableDictionary new];
|
133 | }
|
134 | return _shadowViewRegistry;
|
135 | }
|
136 |
|
137 | - (NSMutableDictionary<NSNumber *, RCTPlatformView *> *)viewRegistry // TODO(macOS ISS#2323203)
|
138 | {
|
139 | // NOTE: this method only exists so that it can be accessed by unit tests
|
140 | if (!_viewRegistry) {
|
141 | _viewRegistry = [NSMutableDictionary new];
|
142 | }
|
143 | return _viewRegistry;
|
144 | }
|
145 |
|
146 | - (NSMapTable *)nativeIDRegistry
|
147 | {
|
148 | if (!_nativeIDRegistry) {
|
149 | _nativeIDRegistry = [NSMapTable strongToWeakObjectsMapTable];
|
150 | }
|
151 | return _nativeIDRegistry;
|
152 | }
|
153 |
|
154 | - (void)setBridge:(RCTBridge *)bridge
|
155 | {
|
156 | RCTAssert(_bridge == nil, @"Should not re-use same UIManager instance");
|
157 | _bridge = bridge;
|
158 |
|
159 | _shadowViewRegistry = [NSMutableDictionary new];
|
160 | _viewRegistry = [NSMutableDictionary new];
|
161 | _nativeIDRegistry = [NSMapTable strongToWeakObjectsMapTable];
|
162 |
|
163 | _shadowViewsWithUpdatedProps = [NSMapTable weakToStrongObjectsMapTable];
|
164 | _shadowViewsWithUpdatedChildren = [NSHashTable weakObjectsHashTable];
|
165 |
|
166 | // Internal resources
|
167 | _pendingUIBlocks = [NSMutableArray new];
|
168 | _rootViewTags = [NSMutableSet new];
|
169 |
|
170 | _observerCoordinator = [RCTUIManagerObserverCoordinator new];
|
171 |
|
172 | // Get view managers from bridge=
|
173 | _componentDataByName = [NSMutableDictionary new];
|
174 | for (Class moduleClass in _bridge.moduleClasses) {
|
175 | if ([moduleClass isSubclassOfClass:[RCTViewManager class]]) {
|
176 | RCTComponentData *componentData = [[RCTComponentData alloc] initWithManagerClass:moduleClass bridge:_bridge];
|
177 | _componentDataByName[componentData.name] = componentData;
|
178 | }
|
179 | }
|
180 |
|
181 | #if !TARGET_OS_OSX // TODO(macOS ISS#2323203)
|
182 | // This dispatch_async avoids a deadlock while configuring native modules
|
183 | dispatch_async(dispatch_get_main_queue(), ^{
|
184 | [[NSNotificationCenter defaultCenter] addObserver:self
|
185 | selector:@selector(didReceiveNewContentSizeMultiplier)
|
186 | name:@"RCTAccessibilityManagerDidUpdateMultiplierNotification"
|
187 | object:[self->_bridge moduleForName:@"AccessibilityManager"
|
188 | lazilyLoadIfNecessary:YES]];
|
189 | });
|
190 | #endif // TODO(macOS ISS#2323203)
|
191 | #if !TARGET_OS_TV && !TARGET_OS_OSX // TODO(macOS ISS#2323203)
|
192 | [[NSNotificationCenter defaultCenter] addObserver:self
|
193 | selector:@selector(namedOrientationDidChange)
|
194 | name:UIDeviceOrientationDidChangeNotification
|
195 | object:nil];
|
196 | #endif
|
197 | #if !TARGET_OS_OSX // TODO(macOS ISS#2323203)
|
198 | [RCTLayoutAnimation initializeStatics];
|
199 | #endif // TODO(macOS ISS#2323203)
|
200 | }
|
201 |
|
202 | #pragma mark - Event emitting
|
203 |
|
204 | #if !TARGET_OS_OSX // TODO(macOS ISS#2323203)
|
205 | - (void)didReceiveNewContentSizeMultiplier
|
206 | {
|
207 | // Report the event across the bridge.
|
208 | #pragma clang diagnostic push
|
209 | #pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
210 | id multiplier = [[self->_bridge moduleForName:@"AccessibilityManager"
|
211 | lazilyLoadIfNecessary:YES] valueForKey:@"multiplier"];
|
212 | if (multiplier) {
|
213 | [_bridge.eventDispatcher sendDeviceEventWithName:@"didUpdateContentSizeMultiplier" body:multiplier];
|
214 | }
|
215 | #pragma clang diagnostic pop
|
216 |
|
217 | RCTExecuteOnUIManagerQueue(^{
|
218 | [[NSNotificationCenter defaultCenter]
|
219 | postNotificationName:RCTUIManagerWillUpdateViewsDueToContentSizeMultiplierChangeNotification
|
220 | object:self];
|
221 | [self setNeedsLayout];
|
222 | });
|
223 | }
|
224 | #endif // TODO(macOS ISS#2323203)
|
225 |
|
226 | #if !TARGET_OS_TV && !TARGET_OS_OSX // TODO(macOS ISS#2323203)
|
227 | // Names and coordinate system from html5 spec:
|
228 | // https://developer.mozilla.org/en-US/docs/Web/API/Screen.orientation
|
229 | // https://developer.mozilla.org/en-US/docs/Web/API/Screen.lockOrientation
|
230 | static NSDictionary *deviceOrientationEventBody(UIDeviceOrientation orientation)
|
231 | {
|
232 | NSString *name;
|
233 | NSNumber *degrees = @0;
|
234 | BOOL isLandscape = NO;
|
235 | switch (orientation) {
|
236 | case UIDeviceOrientationPortrait:
|
237 | name = @"portrait-primary";
|
238 | break;
|
239 | case UIDeviceOrientationPortraitUpsideDown:
|
240 | name = @"portrait-secondary";
|
241 | degrees = @180;
|
242 | break;
|
243 | case UIDeviceOrientationLandscapeRight:
|
244 | name = @"landscape-primary";
|
245 | degrees = @-90;
|
246 | isLandscape = YES;
|
247 | break;
|
248 | case UIDeviceOrientationLandscapeLeft:
|
249 | name = @"landscape-secondary";
|
250 | degrees = @90;
|
251 | isLandscape = YES;
|
252 | break;
|
253 | case UIDeviceOrientationFaceDown:
|
254 | case UIDeviceOrientationFaceUp:
|
255 | case UIDeviceOrientationUnknown:
|
256 | // Unsupported
|
257 | return nil;
|
258 | }
|
259 | return @{
|
260 | @"name" : name,
|
261 | @"rotationDegrees" : degrees,
|
262 | @"isLandscape" : @(isLandscape),
|
263 | };
|
264 | }
|
265 |
|
266 | - (void)namedOrientationDidChange
|
267 | {
|
268 | NSDictionary *orientationEvent = deviceOrientationEventBody([UIDevice currentDevice].orientation);
|
269 | if (!orientationEvent) {
|
270 | return;
|
271 | }
|
272 |
|
273 | #pragma clang diagnostic push
|
274 | #pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
275 | [_bridge.eventDispatcher sendDeviceEventWithName:@"namedOrientationDidChange" body:orientationEvent];
|
276 | #pragma clang diagnostic pop
|
277 | }
|
278 | #endif
|
279 |
|
280 | - (dispatch_queue_t)methodQueue
|
281 | {
|
282 | return RCTGetUIManagerQueue();
|
283 | }
|
284 |
|
285 | - (void)registerRootViewTag:(NSNumber *)rootTag
|
286 | {
|
287 | RCTAssertUIManagerQueue();
|
288 |
|
289 | RCTAssert(RCTIsReactRootView(rootTag), @"Attempt to register rootTag (%@) which is not actually root tag.", rootTag);
|
290 |
|
291 | RCTAssert(
|
292 | ![_rootViewTags containsObject:rootTag],
|
293 | @"Attempt to register rootTag (%@) which was already registered.",
|
294 | rootTag);
|
295 |
|
296 | [_rootViewTags addObject:rootTag];
|
297 |
|
298 | // Registering root shadow view
|
299 | RCTSurfaceRootShadowView *shadowView = [RCTSurfaceRootShadowView new];
|
300 | shadowView.reactTag = rootTag;
|
301 | _shadowViewRegistry[rootTag] = shadowView;
|
302 |
|
303 | // Registering root view
|
304 | RCTExecuteOnMainQueue(^{
|
305 | RCTSurfaceRootView *rootView = [RCTSurfaceRootView new];
|
306 | rootView.reactTag = rootTag;
|
307 | self->_viewRegistry[rootTag] = rootView;
|
308 | });
|
309 | }
|
310 |
|
311 | - (void)registerRootView:(RCTRootContentView *)rootView
|
312 | {
|
313 | RCTAssertMainQueue();
|
314 |
|
315 | NSNumber *reactTag = rootView.reactTag;
|
316 | RCTAssert(RCTIsReactRootView(reactTag), @"View %@ with tag #%@ is not a root view", rootView, reactTag);
|
317 |
|
318 | RCTPlatformView *existingView = _viewRegistry[reactTag]; // TODO(macOS ISS#2323203)
|
319 | RCTAssert(
|
320 | existingView == nil || existingView == rootView,
|
321 | @"Expect all root views to have unique tag. Added %@ twice",
|
322 | reactTag);
|
323 |
|
324 | CGSize availableSize = rootView.availableSize;
|
325 |
|
326 | // Register view
|
327 | _viewRegistry[reactTag] = rootView;
|
328 |
|
329 | // Register shadow view
|
330 | RCTExecuteOnUIManagerQueue(^{
|
331 | if (!self->_viewRegistry) {
|
332 | return;
|
333 | }
|
334 |
|
335 | RCTRootShadowView *shadowView = [RCTRootShadowView new];
|
336 | shadowView.availableSize = availableSize;
|
337 | shadowView.reactTag = reactTag;
|
338 | shadowView.viewName = NSStringFromClass([rootView class]);
|
339 | self->_shadowViewRegistry[shadowView.reactTag] = shadowView;
|
340 | [self->_rootViewTags addObject:reactTag];
|
341 | });
|
342 | }
|
343 |
|
344 | - (NSString *)viewNameForReactTag:(NSNumber *)reactTag
|
345 | {
|
346 | RCTAssertUIManagerQueue();
|
347 | NSString *name = _shadowViewRegistry[reactTag].viewName;
|
348 | if (name) {
|
349 | return name;
|
350 | }
|
351 |
|
352 | __block RCTPlatformView *view; // TODO(macOS ISS#2323203)
|
353 | RCTUnsafeExecuteOnMainQueueSync(^{
|
354 | view = self->_viewRegistry[reactTag];
|
355 | });
|
356 |
|
357 | #pragma clang diagnostic push
|
358 | #pragma clang diagnostic ignored "-Wundeclared-selector"
|
359 |
|
360 | if ([view respondsToSelector:@selector(componentViewName_DO_NOT_USE_THIS_IS_BROKEN)]) {
|
361 | return [view performSelector:@selector(componentViewName_DO_NOT_USE_THIS_IS_BROKEN)];
|
362 | }
|
363 |
|
364 | #pragma clang diagnostic pop
|
365 | return nil;
|
366 | }
|
367 |
|
368 | - (RCTPlatformView *)viewForReactTag:(NSNumber *)reactTag // TODO(macOS ISS#2323203)
|
369 | {
|
370 | RCTAssertMainQueue();
|
371 | return _viewRegistry[reactTag];
|
372 | }
|
373 |
|
374 | - (RCTShadowView *)shadowViewForReactTag:(NSNumber *)reactTag
|
375 | {
|
376 | #if !TARGET_OS_OSX // TODO(macOS ISS#2323203)
|
377 | RCTAssertUIManagerQueue();
|
378 | #endif // TODO(macOS ISS#2323203)
|
379 | return _shadowViewRegistry[reactTag];
|
380 | }
|
381 |
|
382 | - (void)_executeBlockWithShadowView:(void (^)(RCTShadowView *shadowView))block forTag:(NSNumber *)tag
|
383 | {
|
384 | RCTAssertMainQueue();
|
385 |
|
386 | RCTExecuteOnUIManagerQueue(^{
|
387 | RCTShadowView *shadowView = self->_shadowViewRegistry[tag];
|
388 |
|
389 | if (shadowView == nil) {
|
390 | RCTLogInfo(
|
391 | @"Could not locate shadow view with tag #%@, this is probably caused by a temporary inconsistency between native views and shadow views.",
|
392 | tag);
|
393 | return;
|
394 | }
|
395 |
|
396 | block(shadowView);
|
397 | });
|
398 | }
|
399 |
|
400 | - (void)setAvailableSize:(CGSize)availableSize forRootView:(RCTUIView *)rootView // TODO(macOS ISS#3536887)
|
401 | {
|
402 | RCTAssertMainQueue();
|
403 | [self
|
404 | _executeBlockWithShadowView:^(RCTShadowView *shadowView) {
|
405 | RCTAssert(
|
406 | [shadowView isKindOfClass:[RCTRootShadowView class]], @"Located shadow view is actually not root view.");
|
407 |
|
408 | RCTRootShadowView *rootShadowView = (RCTRootShadowView *)shadowView;
|
409 |
|
410 | if (CGSizeEqualToSize(availableSize, rootShadowView.availableSize)) {
|
411 | return;
|
412 | }
|
413 |
|
414 | rootShadowView.availableSize = availableSize;
|
415 | [self setNeedsLayout];
|
416 | }
|
417 | forTag:rootView.reactTag];
|
418 | }
|
419 |
|
420 | - (void)setLocalData:(NSObject *)localData forView:(RCTUIView *)view // TODO(macOS ISS#3536887)
|
421 | {
|
422 | RCTAssertMainQueue();
|
423 | [self
|
424 | _executeBlockWithShadowView:^(RCTShadowView *shadowView) {
|
425 | shadowView.localData = localData;
|
426 | [self setNeedsLayout];
|
427 | }
|
428 | forTag:view.reactTag];
|
429 | }
|
430 |
|
431 | - (RCTUIView *)viewForNativeID:(NSString *)nativeID withRootTag:(NSNumber *)rootTag
|
432 | {
|
433 | if (!nativeID || !rootTag) {
|
434 | return nil;
|
435 | }
|
436 | RCTUIView *view; // TODO(macOS ISS#3536887)
|
437 | @synchronized(self) {
|
438 | view = [_nativeIDRegistry objectForKey:RCTNativeIDRegistryKey(nativeID, rootTag)];
|
439 | }
|
440 | return view;
|
441 | }
|
442 |
|
443 | - (void)setNativeID:(NSString *)nativeID forView:(RCTUIView *)view // TODO(macOS ISS#3536887)
|
444 | {
|
445 | if (!nativeID || !view) {
|
446 | return;
|
447 | }
|
448 | __weak RCTUIManager *weakSelf = self;
|
449 | RCTExecuteOnUIManagerQueue(^{
|
450 | NSNumber *rootTag = [weakSelf shadowViewForReactTag:view.reactTag].rootView.reactTag;
|
451 | @synchronized(weakSelf) {
|
452 | [weakSelf.nativeIDRegistry setObject:view forKey:RCTNativeIDRegistryKey(nativeID, rootTag)];
|
453 | }
|
454 | });
|
455 | }
|
456 |
|
457 | - (void)setSize:(CGSize)size forView:(RCTUIView *)view // TODO(macOS ISS#3536887)
|
458 | {
|
459 | RCTAssertMainQueue();
|
460 | [self
|
461 | _executeBlockWithShadowView:^(RCTShadowView *shadowView) {
|
462 | if (CGSizeEqualToSize(size, shadowView.size)) {
|
463 | return;
|
464 | }
|
465 |
|
466 | shadowView.size = size;
|
467 | [self setNeedsLayout];
|
468 | }
|
469 | forTag:view.reactTag];
|
470 | }
|
471 |
|
472 | - (void)setIntrinsicContentSize:(CGSize)intrinsicContentSize forView:(RCTUIView *)view // TODO(macOS ISS#3536887)
|
473 | {
|
474 | RCTAssertMainQueue();
|
475 | [self
|
476 | _executeBlockWithShadowView:^(RCTShadowView *shadowView) {
|
477 | if (CGSizeEqualToSize(shadowView.intrinsicContentSize, intrinsicContentSize)) {
|
478 | return;
|
479 | }
|
480 |
|
481 | shadowView.intrinsicContentSize = intrinsicContentSize;
|
482 | [self setNeedsLayout];
|
483 | }
|
484 | forTag:view.reactTag];
|
485 | }
|
486 |
|
487 |
|
488 |
|
489 |
|
490 | - (void)_purgeChildren:(NSArray<id<RCTComponent>> *)children
|
491 | fromRegistry:(NSMutableDictionary<NSNumber *, id<RCTComponent>> *)registry
|
492 | {
|
493 | for (id<RCTComponent> child in children) {
|
494 | RCTTraverseViewNodes(registry[child.reactTag], ^(id<RCTComponent> subview) {
|
495 | RCTAssert(![subview isReactRootView], @"Root views should not be unregistered");
|
496 | if ([subview conformsToProtocol:@protocol(RCTInvalidating)]) {
|
497 | [(id<RCTInvalidating>)subview invalidate];
|
498 | }
|
499 | [registry removeObjectForKey:subview.reactTag];
|
500 | });
|
501 | }
|
502 | }
|
503 |
|
504 | - (void)addUIBlock:(RCTViewManagerUIBlock)block
|
505 | {
|
506 | RCTAssertUIManagerQueue();
|
507 |
|
508 | if (!block || !_viewRegistry) {
|
509 | return;
|
510 | }
|
511 |
|
512 | [_pendingUIBlocks addObject:block];
|
513 | }
|
514 |
|
515 | - (void)prependUIBlock:(RCTViewManagerUIBlock)block
|
516 | {
|
517 | RCTAssertUIManagerQueue();
|
518 |
|
519 | if (!block || !_viewRegistry) {
|
520 | return;
|
521 | }
|
522 |
|
523 | [_pendingUIBlocks insertObject:block atIndex:0];
|
524 | }
|
525 |
|
526 | - (void)setNextLayoutAnimationGroup:(RCTLayoutAnimationGroup *)layoutAnimationGroup
|
527 | {
|
528 | RCTAssertMainQueue();
|
529 |
|
530 | if (_layoutAnimationGroup && ![_layoutAnimationGroup isEqual:layoutAnimationGroup]) {
|
531 | RCTLogWarn(
|
532 | @"Warning: Overriding previous layout animation with new one before the first began:\n%@ -> %@.",
|
533 | [_layoutAnimationGroup description],
|
534 | [layoutAnimationGroup description]);
|
535 | }
|
536 |
|
537 | _layoutAnimationGroup = layoutAnimationGroup;
|
538 | }
|
539 |
|
540 | - (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTRootShadowView *)rootShadowView
|
541 | {
|
542 | RCTAssertUIManagerQueue();
|
543 |
|
544 | NSHashTable<RCTShadowView *> *affectedShadowViews = [NSHashTable weakObjectsHashTable];
|
545 | [rootShadowView layoutWithAffectedShadowViews:affectedShadowViews];
|
546 |
|
547 | if (!affectedShadowViews.count) {
|
548 | // no frame change results in no UI update block
|
549 | return nil;
|
550 | }
|
551 |
|
552 | typedef struct {
|
553 | CGRect frame;
|
554 | UIUserInterfaceLayoutDirection layoutDirection;
|
555 | BOOL isNew;
|
556 | BOOL parentIsNew;
|
557 | RCTDisplayType displayType;
|
558 | } RCTFrameData;
|
559 |
|
560 | // Construct arrays then hand off to main thread
|
561 | NSUInteger count = affectedShadowViews.count;
|
562 | NSMutableArray *reactTags = [[NSMutableArray alloc] initWithCapacity:count];
|
563 | NSMutableData *framesData = [[NSMutableData alloc] initWithLength:sizeof(RCTFrameData) * count];
|
564 | {
|
565 | NSUInteger index = 0;
|
566 | RCTFrameData *frameDataArray = (RCTFrameData *)framesData.mutableBytes;
|
567 | for (RCTShadowView *shadowView in affectedShadowViews) {
|
568 | reactTags[index] = shadowView.reactTag;
|
569 | RCTLayoutMetrics layoutMetrics = shadowView.layoutMetrics;
|
570 | frameDataArray[index++] = (RCTFrameData){layoutMetrics.frame,
|
571 | layoutMetrics.layoutDirection,
|
572 | shadowView.isNewView,
|
573 | shadowView.superview.isNewView,
|
574 | layoutMetrics.displayType};
|
575 | }
|
576 | }
|
577 |
|
578 | for (RCTShadowView *shadowView in affectedShadowViews) {
|
579 | // We have to do this after we build the parentsAreNew array.
|
580 | shadowView.newView = NO;
|
581 |
|
582 | NSNumber *reactTag = shadowView.reactTag;
|
583 |
|
584 | if (shadowView.onLayout) {
|
585 | CGRect frame = shadowView.layoutMetrics.frame;
|
586 | shadowView.onLayout(@{
|
587 | @"layout" : @{
|
588 | @"x" : @(frame.origin.x),
|
589 | @"y" : @(frame.origin.y),
|
590 | @"width" : @(frame.size.width),
|
591 | @"height" : @(frame.size.height),
|
592 | },
|
593 | });
|
594 | }
|
595 |
|
596 | if (RCTIsReactRootView(reactTag) && [shadowView isKindOfClass:[RCTRootShadowView class]]) {
|
597 | CGSize contentSize = shadowView.layoutMetrics.frame.size;
|
598 |
|
599 | RCTExecuteOnMainQueue(^{
|
600 | RCTPlatformView *view = self->_viewRegistry[reactTag]; // TODO(macOS ISS#2323203)
|
601 | RCTAssert(view != nil, @"view (for ID %@) not found", reactTag);
|
602 |
|
603 | RCTRootView *rootView = (RCTRootView *)[view superview];
|
604 | if ([rootView isKindOfClass:[RCTRootView class]]) {
|
605 | rootView.intrinsicContentSize = contentSize;
|
606 | }
|
607 | });
|
608 | }
|
609 | }
|
610 |
|
611 | // Perform layout (possibly animated)
|
612 | return ^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTPlatformView *> *viewRegistry) { // TODO(macOS ISS#2323203)
|
613 | const RCTFrameData *frameDataArray = (const RCTFrameData *)framesData.bytes;
|
614 | RCTLayoutAnimationGroup *layoutAnimationGroup = uiManager->_layoutAnimationGroup;
|
615 |
|
616 | __block NSUInteger completionsCalled = 0;
|
617 |
|
618 | NSInteger index = 0;
|
619 | for (NSNumber *reactTag in reactTags) {
|
620 | RCTFrameData frameData = frameDataArray[index++];
|
621 |
|
622 | RCTPlatformView *view = viewRegistry[reactTag]; // TODO(macOS ISS#2323203)
|
623 | CGRect frame = frameData.frame;
|
624 |
|
625 | UIUserInterfaceLayoutDirection layoutDirection = frameData.layoutDirection;
|
626 | BOOL isNew = frameData.isNew;
|
627 | RCTLayoutAnimation *updatingLayoutAnimation = isNew ? nil : layoutAnimationGroup.updatingLayoutAnimation;
|
628 | BOOL shouldAnimateCreation = isNew && !frameData.parentIsNew;
|
629 | RCTLayoutAnimation *creatingLayoutAnimation =
|
630 | shouldAnimateCreation ? layoutAnimationGroup.creatingLayoutAnimation : nil;
|
631 | BOOL isHidden = frameData.displayType == RCTDisplayTypeNone;
|
632 |
|
633 | void (^completion)(BOOL) = ^(BOOL finished) {
|
634 | completionsCalled++;
|
635 | if (layoutAnimationGroup.callback && completionsCalled == count) {
|
636 | layoutAnimationGroup.callback(@[ @(finished) ]);
|
637 |
|
638 | // It's unsafe to call this callback more than once, so we nil it out here
|
639 | // to make sure that doesn't happen.
|
640 | layoutAnimationGroup.callback = nil;
|
641 | }
|
642 | };
|
643 |
|
644 | if (view.reactLayoutDirection != layoutDirection) {
|
645 | view.reactLayoutDirection = layoutDirection;
|
646 | }
|
647 |
|
648 | if (view.isHidden != isHidden) {
|
649 | view.hidden = isHidden;
|
650 | }
|
651 |
|
652 | if (creatingLayoutAnimation) {
|
653 | // Animate view creation
|
654 | [view reactSetFrame:frame];
|
655 |
|
656 | CATransform3D finalTransform = view.layer.transform;
|
657 | CGFloat finalOpacity = view.layer.opacity;
|
658 |
|
659 | NSString *property = creatingLayoutAnimation.property;
|
660 | if ([property isEqualToString:@"scaleXY"]) {
|
661 | view.layer.transform = CATransform3DMakeScale(0, 0, 0);
|
662 | } else if ([property isEqualToString:@"scaleX"]) {
|
663 | view.layer.transform = CATransform3DMakeScale(0, 1, 0);
|
664 | } else if ([property isEqualToString:@"scaleY"]) {
|
665 | view.layer.transform = CATransform3DMakeScale(1, 0, 0);
|
666 | } else if ([property isEqualToString:@"opacity"]) {
|
667 | view.layer.opacity = 0.0;
|
668 | } else {
|
669 | RCTLogError(@"Unsupported layout animation createConfig property %@", creatingLayoutAnimation.property);
|
670 | }
|
671 |
|
672 | [creatingLayoutAnimation
|
673 | performAnimations:^{
|
674 | if ([property isEqualToString:@"scaleX"] || [property isEqualToString:@"scaleY"] ||
|
675 | [property isEqualToString:@"scaleXY"]) {
|
676 | view.layer.transform = finalTransform;
|
677 | } else if ([property isEqualToString:@"opacity"]) {
|
678 | view.layer.opacity = finalOpacity;
|
679 | }
|
680 | }
|
681 | withCompletionBlock:completion];
|
682 |
|
683 | } else if (updatingLayoutAnimation) {
|
684 | // Animate view update
|
685 | [updatingLayoutAnimation
|
686 | performAnimations:^{
|
687 | [view reactSetFrame:frame];
|
688 | }
|
689 | withCompletionBlock:completion];
|
690 |
|
691 | } else {
|
692 | // Update without animation
|
693 | [view reactSetFrame:frame];
|
694 | completion(YES);
|
695 | }
|
696 | }
|
697 |
|
698 | // Clean up
|
699 | uiManager->_layoutAnimationGroup = nil;
|
700 | };
|
701 | }
|
702 |
|
703 |
|
704 |
|
705 |
|
706 |
|
707 | RCT_EXPORT_METHOD(removeSubviewsFromContainerWithID : (nonnull NSNumber *)containerID)
|
708 | {
|
709 | RCTLogWarn(
|
710 | @"RCTUIManager.removeSubviewsFromContainerWithID method is deprecated and it will not be implemented in newer versions of RN (Fabric) - T47686450");
|
711 | id<RCTComponent> container = _shadowViewRegistry[containerID];
|
712 | RCTAssert(container != nil, @"container view (for ID %@) not found", containerID);
|
713 |
|
714 | NSUInteger subviewsCount = [container reactSubviews].count;
|
715 | NSMutableArray<NSNumber *> *indices = [[NSMutableArray alloc] initWithCapacity:subviewsCount];
|
716 | for (NSUInteger childIndex = 0; childIndex < subviewsCount; childIndex++) {
|
717 | [indices addObject:@(childIndex)];
|
718 | }
|
719 |
|
720 | [self manageChildren:containerID
|
721 | moveFromIndices:nil
|
722 | moveToIndices:nil
|
723 | addChildReactTags:nil
|
724 | addAtIndices:nil
|
725 | removeAtIndices:indices];
|
726 | }
|
727 |
|
728 |
|
729 |
|
730 |
|
731 |
|
732 |
|
733 |
|
734 | - (NSArray<id<RCTComponent>> *)_childrenToRemoveFromContainer:(id<RCTComponent>)container
|
735 | atIndices:(NSArray<NSNumber *> *)atIndices
|
736 | {
|
737 | // If there are no indices to move or the container has no subviews don't bother
|
738 | // We support parents with nil subviews so long as they're all nil so this allows for this behavior
|
739 | if (atIndices.count == 0 || [container reactSubviews].count == 0) {
|
740 | return nil;
|
741 | }
|
742 | // Construction of removed children must be done "up front", before indices are disturbed by removals.
|
743 | NSMutableArray<id<RCTComponent>> *removedChildren = [NSMutableArray arrayWithCapacity:atIndices.count];
|
744 | RCTAssert(container != nil, @"container view (for ID %@) not found", container);
|
745 | for (NSNumber *indexNumber in atIndices) {
|
746 | NSUInteger index = indexNumber.unsignedIntegerValue;
|
747 | if (index < [container reactSubviews].count) {
|
748 | [removedChildren addObject:[container reactSubviews][index]];
|
749 | }
|
750 | }
|
751 | if (removedChildren.count != atIndices.count) {
|
752 | NSString *message = [NSString stringWithFormat:@"removedChildren count (%tu) was not what we expected (%tu)",
|
753 | removedChildren.count,
|
754 | atIndices.count];
|
755 | RCTFatal(RCTErrorWithMessage(message));
|
756 | }
|
757 | return removedChildren;
|
758 | }
|
759 |
|
760 | - (void)_removeChildren:(NSArray<id<RCTComponent>> *)children fromContainer:(id<RCTComponent>)container
|
761 | {
|
762 | for (id<RCTComponent> removedChild in children) {
|
763 | [container removeReactSubview:removedChild];
|
764 | }
|
765 | }
|
766 |
|
767 |
|
768 |
|
769 |
|
770 | - (void)_removeChildren:(NSArray<RCTPlatformView *> *)children // TODO(macOS ISS#2323203)
|
771 | fromContainer:(RCTPlatformView *)container // TODO(macOS ISS#2323203)
|
772 | withAnimation:(RCTLayoutAnimationGroup *)animation
|
773 | {
|
774 | RCTAssertMainQueue();
|
775 | RCTLayoutAnimation *deletingLayoutAnimation = animation.deletingLayoutAnimation;
|
776 |
|
777 | __block NSUInteger completionsCalled = 0;
|
778 | for (RCTPlatformView *removedChild in children) { // TODO(macOS ISS#2323203)
|
779 | void (^completion)(BOOL) = ^(BOOL finished) {
|
780 | completionsCalled++;
|
781 |
|
782 | [removedChild removeFromSuperview];
|
783 |
|
784 | if (animation.callback && completionsCalled == children.count) {
|
785 | animation.callback(@[ @(finished) ]);
|
786 |
|
787 | // It's unsafe to call this callback more than once, so we nil it out here
|
788 | // to make sure that doesn't happen.
|
789 | animation.callback = nil;
|
790 | }
|
791 | };
|
792 |
|
793 | // Hack: At this moment we have two contradict intents.
|
794 | // First one: We want to delete the view from view hierarchy.
|
795 | // Second one: We want to animate this view, which implies the existence of this view in the hierarchy.
|
796 | // So, we have to remove this view from React's view hierarchy but postpone removing from UIKit's hierarchy.
|
797 | // Here the problem: the default implementation of `-[UIView removeReactSubview:]` also removes the view from
|
798 | // UIKit's hierarchy. So, let's temporary restore the view back after removing. To do so, we have to memorize
|
799 | // original `superview` (which can differ from `container`) and an index of removed view.
|
800 | RCTPlatformView *originalSuperview = removedChild.superview; // TODO(macOS ISS#2323203)
|
801 | NSUInteger originalIndex = [originalSuperview.subviews indexOfObjectIdenticalTo:removedChild];
|
802 | #if TARGET_OS_OSX // [TODO(macOS ISS#2323203)
|
803 | NSView *nextLowerView = nil;
|
804 | if (originalIndex > 0) {
|
805 | nextLowerView = [originalSuperview.subviews objectAtIndex:originalIndex - 1];
|
806 | }
|
807 | #endif // ]TODO(macOS ISS#2323203)
|
808 | [container removeReactSubview:removedChild];
|
809 | // Disable user interaction while the view is animating
|
810 | // since the view is (conceptually) deleted and not supposed to be interactive.
|
811 | if ([removedChild respondsToSelector:@selector(setUserInteractionEnabled:)]) { // [TODO(macOS ISS#2323203)
|
812 | ((RCTUIView *)removedChild).userInteractionEnabled = NO; // TODO(macOS ISS#3536887)
|
813 | }
|
814 | #if !TARGET_OS_OSX // ]TODO(macOS ISS#2323203)
|
815 | [originalSuperview insertSubview:removedChild atIndex:originalIndex];
|
816 | #else // [TODO(macOS ISS#2323203)
|
817 | [originalSuperview addSubview:removedChild positioned:nextLowerView == nil ? NSWindowBelow : NSWindowAbove relativeTo:nextLowerView];
|
818 | #endif // ]TODO(macOS ISS#2323203)
|
819 |
|
820 | NSString *property = deletingLayoutAnimation.property;
|
821 | [deletingLayoutAnimation
|
822 | performAnimations:^{
|
823 | if ([property isEqualToString:@"scaleXY"]) {
|
824 | removedChild.layer.transform = CATransform3DMakeScale(0.001, 0.001, 0.001);
|
825 | } else if ([property isEqualToString:@"scaleX"]) {
|
826 | removedChild.layer.transform = CATransform3DMakeScale(0.001, 1, 0.001);
|
827 | } else if ([property isEqualToString:@"scaleY"]) {
|
828 | removedChild.layer.transform = CATransform3DMakeScale(1, 0.001, 0.001);
|
829 | } else if ([property isEqualToString:@"opacity"]) {
|
830 | removedChild.layer.opacity = 0.0;
|
831 | } else {
|
832 | RCTLogError(@"Unsupported layout animation createConfig property %@", deletingLayoutAnimation.property);
|
833 | }
|
834 | }
|
835 | withCompletionBlock:completion];
|
836 | }
|
837 | }
|
838 |
|
839 | RCT_EXPORT_METHOD(removeRootView : (nonnull NSNumber *)rootReactTag)
|
840 | {
|
841 | RCTShadowView *rootShadowView = _shadowViewRegistry[rootReactTag];
|
842 | RCTAssert(rootShadowView.superview == nil, @"root view cannot have superview (ID %@)", rootReactTag);
|
843 | [self _purgeChildren:(NSArray<id<RCTComponent>> *)rootShadowView.reactSubviews
|
844 | fromRegistry:(NSMutableDictionary<NSNumber *, id<RCTComponent>> *)_shadowViewRegistry];
|
845 | [_shadowViewRegistry removeObjectForKey:rootReactTag];
|
846 | [_rootViewTags removeObject:rootReactTag];
|
847 |
|
848 | [self addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTPlatformView *> *viewRegistry) { // TODO(macOS ISS#2323203)
|
849 | RCTAssertMainQueue();
|
850 | RCTPlatformView *rootView = viewRegistry[rootReactTag]; // TODO(macOS ISS#2323203)
|
851 | [uiManager _purgeChildren:(NSArray<id<RCTComponent>> *)rootView.reactSubviews
|
852 | fromRegistry:(NSMutableDictionary<NSNumber *, id<RCTComponent>> *)viewRegistry];
|
853 | [(NSMutableDictionary *)viewRegistry removeObjectForKey:rootReactTag];
|
854 | }];
|
855 | }
|
856 |
|
857 | RCT_EXPORT_METHOD(replaceExistingNonRootView : (nonnull NSNumber *)reactTag withView : (nonnull NSNumber *)newReactTag)
|
858 | {
|
859 | RCTLogWarn(
|
860 | @"RCTUIManager.replaceExistingNonRootView method is deprecated and it will not be implemented in newer versions of RN (Fabric) - T47686450");
|
861 | RCTShadowView *shadowView = _shadowViewRegistry[reactTag];
|
862 | RCTAssert(shadowView != nil, @"shadowView (for ID %@) not found", reactTag);
|
863 |
|
864 | RCTShadowView *superShadowView = shadowView.superview;
|
865 | if (!superShadowView) {
|
866 | RCTAssert(NO, @"shadowView super (of ID %@) not found", reactTag);
|
867 | return;
|
868 | }
|
869 |
|
870 | NSUInteger indexOfView = [superShadowView.reactSubviews indexOfObjectIdenticalTo:shadowView];
|
871 | RCTAssert(indexOfView != NSNotFound, @"View's superview doesn't claim it as subview (id %@)", reactTag);
|
872 | NSArray<NSNumber *> *removeAtIndices = @[ @(indexOfView) ];
|
873 | NSArray<NSNumber *> *addTags = @[ newReactTag ];
|
874 | [self manageChildren:superShadowView.reactTag
|
875 | moveFromIndices:nil
|
876 | moveToIndices:nil
|
877 | addChildReactTags:addTags
|
878 | addAtIndices:removeAtIndices
|
879 | removeAtIndices:removeAtIndices];
|
880 | }
|
881 |
|
882 | RCT_EXPORT_METHOD(setChildren : (nonnull NSNumber *)containerTag reactTags : (NSArray<NSNumber *> *)reactTags)
|
883 | {
|
884 | RCTSetChildren(containerTag, reactTags, (NSDictionary<NSNumber *, id<RCTComponent>> *)_shadowViewRegistry);
|
885 |
|
886 | [self addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTPlatformView *> *viewRegistry) { // TODO(macOS ISS#2323203)
|
887 | RCTSetChildren(containerTag, reactTags, (NSDictionary<NSNumber *, id<RCTComponent>> *)viewRegistry);
|
888 | }];
|
889 |
|
890 | [self _shadowViewDidReceiveUpdatedChildren:_shadowViewRegistry[containerTag]];
|
891 | }
|
892 |
|
893 | static void RCTSetChildren(
|
894 | NSNumber *containerTag,
|
895 | NSArray<NSNumber *> *reactTags,
|
896 | NSDictionary<NSNumber *, id<RCTComponent>> *registry)
|
897 | {
|
898 | id<RCTComponent> container = registry[containerTag];
|
899 | NSInteger index = 0;
|
900 | for (NSNumber *reactTag in reactTags) {
|
901 | id<RCTComponent> view = registry[reactTag];
|
902 | if (view) {
|
903 | [container insertReactSubview:view atIndex:index++];
|
904 | }
|
905 | }
|
906 | }
|
907 |
|
908 | RCT_EXPORT_METHOD(manageChildren
|
909 | : (nonnull NSNumber *)containerTag moveFromIndices
|
910 | : (NSArray<NSNumber *> *)moveFromIndices moveToIndices
|
911 | : (NSArray<NSNumber *> *)moveToIndices addChildReactTags
|
912 | : (NSArray<NSNumber *> *)addChildReactTags addAtIndices
|
913 | : (NSArray<NSNumber *> *)addAtIndices removeAtIndices
|
914 | : (NSArray<NSNumber *> *)removeAtIndices)
|
915 | {
|
916 | [self _manageChildren:containerTag
|
917 | moveFromIndices:moveFromIndices
|
918 | moveToIndices:moveToIndices
|
919 | addChildReactTags:addChildReactTags
|
920 | addAtIndices:addAtIndices
|
921 | removeAtIndices:removeAtIndices
|
922 | registry:(NSMutableDictionary<NSNumber *, id<RCTComponent>> *)_shadowViewRegistry];
|
923 |
|
924 | [self addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTPlatformView *> *viewRegistry) { // TODO(macOS ISS#2323203)
|
925 | [uiManager _manageChildren:containerTag
|
926 | moveFromIndices:moveFromIndices
|
927 | moveToIndices:moveToIndices
|
928 | addChildReactTags:addChildReactTags
|
929 | addAtIndices:addAtIndices
|
930 | removeAtIndices:removeAtIndices
|
931 | registry:(NSMutableDictionary<NSNumber *, id<RCTComponent>> *)viewRegistry];
|
932 | }];
|
933 |
|
934 | [self _shadowViewDidReceiveUpdatedChildren:_shadowViewRegistry[containerTag]];
|
935 | }
|
936 |
|
937 | - (void)_manageChildren:(NSNumber *)containerTag
|
938 | moveFromIndices:(NSArray<NSNumber *> *)moveFromIndices
|
939 | moveToIndices:(NSArray<NSNumber *> *)moveToIndices
|
940 | addChildReactTags:(NSArray<NSNumber *> *)addChildReactTags
|
941 | addAtIndices:(NSArray<NSNumber *> *)addAtIndices
|
942 | removeAtIndices:(NSArray<NSNumber *> *)removeAtIndices
|
943 | registry:(NSMutableDictionary<NSNumber *, id<RCTComponent>> *)registry
|
944 | {
|
945 | id<RCTComponent> container = registry[containerTag];
|
946 | RCTAssert(
|
947 | moveFromIndices.count == moveToIndices.count,
|
948 | @"moveFromIndices had size %tu, moveToIndices had size %tu",
|
949 | moveFromIndices.count,
|
950 | moveToIndices.count);
|
951 | RCTAssert(addChildReactTags.count == addAtIndices.count, @"there should be at least one React child to add");
|
952 |
|
953 | // Removes (both permanent and temporary moves) are using "before" indices
|
954 | NSArray<id<RCTComponent>> *permanentlyRemovedChildren = [self _childrenToRemoveFromContainer:container
|
955 | atIndices:removeAtIndices];
|
956 | NSArray<id<RCTComponent>> *temporarilyRemovedChildren = [self _childrenToRemoveFromContainer:container
|
957 | atIndices:moveFromIndices];
|
958 |
|
959 | BOOL isUIViewRegistry = ((id)registry == (id)_viewRegistry);
|
960 | if (isUIViewRegistry && _layoutAnimationGroup.deletingLayoutAnimation) {
|
961 | [self _removeChildren:(NSArray<RCTPlatformView *> *)permanentlyRemovedChildren // TODO(macOS ISS#2323203)
|
962 | fromContainer:(RCTPlatformView *)container // TODO(macOS ISS#2323203)
|
963 | withAnimation:_layoutAnimationGroup];
|
964 | } else {
|
965 | [self _removeChildren:permanentlyRemovedChildren fromContainer:container];
|
966 | }
|
967 |
|
968 | [self _removeChildren:temporarilyRemovedChildren fromContainer:container];
|
969 | [self _purgeChildren:permanentlyRemovedChildren fromRegistry:registry];
|
970 |
|
971 | // Figure out what to insert - merge temporary inserts and adds
|
972 | NSMutableDictionary *destinationsToChildrenToAdd = [NSMutableDictionary dictionary];
|
973 | for (NSInteger index = 0, length = temporarilyRemovedChildren.count; index < length; index++) {
|
974 | destinationsToChildrenToAdd[moveToIndices[index]] = temporarilyRemovedChildren[index];
|
975 | }
|
976 |
|
977 | for (NSInteger index = 0, length = addAtIndices.count; index < length; index++) {
|
978 | id<RCTComponent> view = registry[addChildReactTags[index]];
|
979 | if (view) {
|
980 | destinationsToChildrenToAdd[addAtIndices[index]] = view;
|
981 | }
|
982 | }
|
983 |
|
984 | NSArray<NSNumber *> *sortedIndices =
|
985 | [destinationsToChildrenToAdd.allKeys sortedArrayUsingSelector:@selector(compare:)];
|
986 | for (NSNumber *reactIndex in sortedIndices) {
|
987 | [container insertReactSubview:destinationsToChildrenToAdd[reactIndex] atIndex:reactIndex.integerValue];
|
988 | }
|
989 | }
|
990 |
|
991 | RCT_EXPORT_METHOD(createView
|
992 | : (nonnull NSNumber *)reactTag viewName
|
993 | : (NSString *)viewName rootTag
|
994 | : (nonnull NSNumber *)rootTag props
|
995 | : (NSDictionary *)props)
|
996 | {
|
997 | RCTComponentData *componentData = _componentDataByName[viewName];
|
998 | if (componentData == nil) {
|
999 | RCTLogError(@"No component found for view with name \"%@\"", viewName);
|
1000 | }
|
1001 |
|
1002 | // Register shadow view
|
1003 | RCTShadowView *shadowView = [componentData createShadowViewWithTag:reactTag];
|
1004 | if (shadowView) {
|
1005 | [componentData setProps:props forShadowView:shadowView];
|
1006 | _shadowViewRegistry[reactTag] = shadowView;
|
1007 | RCTShadowView *rootView = _shadowViewRegistry[rootTag];
|
1008 | RCTAssert(
|
1009 | [rootView isKindOfClass:[RCTRootShadowView class]]
|
1010 | || [rootView isKindOfClass:[RCTSurfaceRootShadowView class]]
|
1011 | ,
|
1012 | @"Given `rootTag` (%@) does not correspond to a valid root shadow view instance.",
|
1013 | rootTag);
|
1014 | shadowView.rootView = (RCTRootShadowView *)rootView;
|
1015 | }
|
1016 |
|
1017 | // Dispatch view creation directly to the main thread instead of adding to
|
1018 | // UIBlocks array. This way, it doesn't get deferred until after layout.
|
1019 | __block RCTPlatformView *preliminaryCreatedView = nil; // TODO(macOS ISS#2323203)
|
1020 |
|
1021 | void (^createViewBlock)(void) = ^{
|
1022 | // Do nothing on the second run.
|
1023 | if (preliminaryCreatedView) {
|
1024 | return;
|
1025 | }
|
1026 |
|
1027 | preliminaryCreatedView = [componentData createViewWithTag:reactTag rootTag:rootTag];
|
1028 |
|
1029 | if (preliminaryCreatedView) {
|
1030 | self->_viewRegistry[reactTag] = preliminaryCreatedView;
|
1031 | }
|
1032 | };
|
1033 |
|
1034 | // We cannot guarantee that asynchronously scheduled block will be executed
|
1035 | // *before* a block is added to the regular mounting process (simply because
|
1036 | // mounting process can be managed externally while the main queue is
|
1037 | // locked).
|
1038 | // So, we positively dispatch it asynchronously and double check inside
|
1039 | // the regular mounting block.
|
1040 |
|
1041 | RCTExecuteOnMainQueue(createViewBlock);
|
1042 |
|
1043 | [self addUIBlock:^(__unused RCTUIManager *uiManager, __unused NSDictionary<NSNumber *, RCTPlatformView *> *viewRegistry) { // TODO(macOS ISS#2323203)
|
1044 | createViewBlock();
|
1045 |
|
1046 | if (preliminaryCreatedView) {
|
1047 | [componentData setProps:props forView:preliminaryCreatedView];
|
1048 | }
|
1049 | }];
|
1050 |
|
1051 | [self _shadowView:shadowView didReceiveUpdatedProps:[props allKeys]];
|
1052 | }
|
1053 |
|
1054 | RCT_EXPORT_METHOD(updateView
|
1055 | : (nonnull NSNumber *)reactTag viewName
|
1056 | : (NSString *)viewName // not always reliable, use shadowView.viewName if available
|
1057 | props
|
1058 | : (NSDictionary *)props)
|
1059 | {
|
1060 | RCTShadowView *shadowView = _shadowViewRegistry[reactTag];
|
1061 | RCTComponentData *componentData = _componentDataByName[shadowView.viewName ?: viewName];
|
1062 | [componentData setProps:props forShadowView:shadowView];
|
1063 |
|
1064 | [self addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTPlatformView *> *viewRegistry) { // TODO(macOS ISS#2323203)
|
1065 | RCTPlatformView *view = viewRegistry[reactTag]; // TODO(macOS ISS#2323203)
|
1066 | [componentData setProps:props forView:view];
|
1067 | }];
|
1068 |
|
1069 | [self _shadowView:shadowView didReceiveUpdatedProps:[props allKeys]];
|
1070 | }
|
1071 |
|
1072 | - (void)synchronouslyUpdateViewOnUIThread:(NSNumber *)reactTag viewName:(NSString *)viewName props:(NSDictionary *)props
|
1073 | {
|
1074 | RCTAssertMainQueue();
|
1075 | RCTComponentData *componentData = _componentDataByName[viewName];
|
1076 | RCTUIView *view = _viewRegistry[reactTag]; // TODO(macOS ISS#3536887)
|
1077 | [componentData setProps:props forView:view];
|
1078 | }
|
1079 |
|
1080 | RCT_EXPORT_METHOD(focus : (nonnull NSNumber *)reactTag)
|
1081 | {
|
1082 | [self addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTUIView *> *viewRegistry) { // TODO(macOS ISS#3536887)
|
1083 | RCTUIView *newResponder = viewRegistry[reactTag]; // TODO(macOS ISS#3536887)
|
1084 | [newResponder reactFocus];
|
1085 | }];
|
1086 | }
|
1087 |
|
1088 | RCT_EXPORT_METHOD(blur : (nonnull NSNumber *)reactTag)
|
1089 | {
|
1090 | [self addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTUIView *> *viewRegistry) { // TODO(macOS ISS#3536887)
|
1091 | RCTUIView *currentResponder = viewRegistry[reactTag]; // TODO(macOS ISS#3536887)
|
1092 | [currentResponder reactBlur];
|
1093 | }];
|
1094 | }
|
1095 |
|
1096 | RCT_EXPORT_METHOD(findSubviewIn
|
1097 | : (nonnull NSNumber *)reactTag atPoint
|
1098 | : (CGPoint)point callback
|
1099 | : (RCTResponseSenderBlock)callback)
|
1100 | {
|
1101 | [self addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTPlatformView *> *viewRegistry) { // TODO(macOS ISS#2323203)
|
1102 | RCTPlatformView *view = viewRegistry[reactTag]; // TODO(macOS ISS#2323203)
|
1103 | RCTPlatformView *target = RCTUIViewHitTestWithEvent(view, point, nil); // TODO(macOS ISS#2323203) and TODO(macOS ISS#3536887)
|
1104 | CGRect frame = [target convertRect:target.bounds toView:view];
|
1105 |
|
1106 | while (target.reactTag == nil && target.superview != nil) {
|
1107 | target = target.superview;
|
1108 | }
|
1109 |
|
1110 | callback(@[
|
1111 | RCTNullIfNil(target.reactTag),
|
1112 | @(frame.origin.x),
|
1113 | @(frame.origin.y),
|
1114 | @(frame.size.width),
|
1115 | @(frame.size.height),
|
1116 | ]);
|
1117 | }];
|
1118 | }
|
1119 |
|
1120 | RCT_EXPORT_METHOD(dispatchViewManagerCommand
|
1121 | : (nonnull NSNumber *)reactTag commandID
|
1122 | : (id )commandID commandArgs
|
1123 | : (NSArray<id> *)commandArgs)
|
1124 | {
|
1125 | RCTShadowView *shadowView = _shadowViewRegistry[reactTag];
|
1126 | RCTComponentData *componentData = _componentDataByName[shadowView.viewName];
|
1127 |
|
1128 | // Achtung! Achtung!
|
1129 | // This is a remarkably hacky and ugly workaround.
|
1130 | // We need this only temporary for some testing. We need this hack until Fabric fully implements command-execution
|
1131 | // pipeline. This does not affect non-Fabric apps.
|
1132 | #pragma clang diagnostic push
|
1133 | #pragma clang diagnostic ignored "-Wundeclared-selector"
|
1134 | if (!componentData) {
|
1135 | __block RCTPlatformView *view; // TODO(macOS ISS#2323203)
|
1136 | RCTUnsafeExecuteOnMainQueueSync(^{
|
1137 | view = self->_viewRegistry[reactTag];
|
1138 | });
|
1139 | if ([view respondsToSelector:@selector(componentViewName_DO_NOT_USE_THIS_IS_BROKEN)]) {
|
1140 | NSString *name = [view performSelector:@selector(componentViewName_DO_NOT_USE_THIS_IS_BROKEN)];
|
1141 | componentData = _componentDataByName[[NSString stringWithFormat:@"RCT%@", name]];
|
1142 | }
|
1143 | }
|
1144 | #pragma clang diagnostic pop
|
1145 |
|
1146 | Class managerClass = componentData.managerClass;
|
1147 | RCTModuleData *moduleData = [_bridge moduleDataForName:RCTBridgeModuleNameForClass(managerClass)];
|
1148 |
|
1149 | id<RCTBridgeMethod> method;
|
1150 | if ([commandID isKindOfClass:[NSNumber class]]) {
|
1151 | method = moduleData.methods[[commandID intValue]];
|
1152 | } else if ([commandID isKindOfClass:[NSString class]]) {
|
1153 | method = moduleData.methodsByName[commandID];
|
1154 | if (method == nil) {
|
1155 | RCTLogError(@"No command found with name \"%@\"", commandID);
|
1156 | }
|
1157 | } else {
|
1158 | RCTLogError(@"dispatchViewManagerCommand must be called with a string or integer command");
|
1159 | return;
|
1160 | }
|
1161 |
|
1162 | NSArray *args = [@[ reactTag ] arrayByAddingObjectsFromArray:commandArgs];
|
1163 | [method invokeWithBridge:_bridge module:componentData.manager arguments:args];
|
1164 | }
|
1165 |
|
1166 | - (void)batchDidComplete
|
1167 | {
|
1168 | [self _layoutAndMount];
|
1169 | }
|
1170 |
|
1171 |
|
1172 |
|
1173 |
|
1174 |
|
1175 | - (void)_layoutAndMount
|
1176 | {
|
1177 | [self _dispatchPropsDidChangeEvents];
|
1178 | [self _dispatchChildrenDidChangeEvents];
|
1179 |
|
1180 | [_observerCoordinator uiManagerWillPerformLayout:self];
|
1181 |
|
1182 | // Perform layout
|
1183 | for (NSNumber *reactTag in _rootViewTags) {
|
1184 | RCTRootShadowView *rootView = (RCTRootShadowView *)_shadowViewRegistry[reactTag];
|
1185 | [self addUIBlock:[self uiBlockWithLayoutUpdateForRootView:rootView]];
|
1186 | }
|
1187 |
|
1188 | [_observerCoordinator uiManagerDidPerformLayout:self];
|
1189 |
|
1190 | [_observerCoordinator uiManagerWillPerformMounting:self];
|
1191 |
|
1192 | [self flushUIBlocksWithCompletion:^{
|
1193 | [self->_observerCoordinator uiManagerDidPerformMounting:self];
|
1194 | }];
|
1195 | }
|
1196 |
|
1197 | - (void)flushUIBlocksWithCompletion:(void (^)(void))completion
|
1198 | {
|
1199 | RCTAssertUIManagerQueue();
|
1200 |
|
1201 | // First copy the previous blocks into a temporary variable, then reset the
|
1202 | // pending blocks to a new array. This guards against mutation while
|
1203 | // processing the pending blocks in another thread.
|
1204 | NSArray<RCTViewManagerUIBlock> *previousPendingUIBlocks = _pendingUIBlocks;
|
1205 | _pendingUIBlocks = [NSMutableArray new];
|
1206 |
|
1207 | if (previousPendingUIBlocks.count == 0) {
|
1208 | completion();
|
1209 | return;
|
1210 | }
|
1211 |
|
1212 | __weak typeof(self) weakSelf = self;
|
1213 |
|
1214 | void (^mountingBlock)(void) = ^{
|
1215 | typeof(self) strongSelf = weakSelf;
|
1216 |
|
1217 | @try {
|
1218 | for (RCTViewManagerUIBlock block in previousPendingUIBlocks) {
|
1219 | block(strongSelf, strongSelf->_viewRegistry);
|
1220 | }
|
1221 | } @catch (NSException *exception) {
|
1222 | RCTLogError(@"Exception thrown while executing UI block: %@", exception);
|
1223 | }
|
1224 | };
|
1225 |
|
1226 | if ([self.observerCoordinator uiManager:self performMountingWithBlock:mountingBlock]) {
|
1227 | completion();
|
1228 | return;
|
1229 | }
|
1230 |
|
1231 | // Execute the previously queued UI blocks
|
1232 | RCTProfileBeginFlowEvent();
|
1233 | RCTExecuteOnMainQueue(^{
|
1234 | RCTProfileEndFlowEvent();
|
1235 | RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[UIManager flushUIBlocks]", (@{
|
1236 | @"count" : [@(previousPendingUIBlocks.count) stringValue],
|
1237 | }));
|
1238 |
|
1239 | mountingBlock();
|
1240 |
|
1241 | RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
|
1242 |
|
1243 | RCTExecuteOnUIManagerQueue(completion);
|
1244 | });
|
1245 | }
|
1246 |
|
1247 | - (void)setNeedsLayout
|
1248 | {
|
1249 | // If there is an active batch layout will happen when batch finished, so we will wait for that.
|
1250 | // Otherwise we immediately trigger layout.
|
1251 | if (![_bridge isBatchActive] && ![_bridge isLoading]) {
|
1252 | [self _layoutAndMount];
|
1253 | }
|
1254 | }
|
1255 |
|
1256 | - (void)_shadowView:(RCTShadowView *)shadowView didReceiveUpdatedProps:(NSArray<NSString *> *)props
|
1257 | {
|
1258 | // We collect a set with changed `shadowViews` and its changed props,
|
1259 | // so we have to maintain this collection properly.
|
1260 | NSArray<NSString *> *previousProps;
|
1261 | if ((previousProps = [_shadowViewsWithUpdatedProps objectForKey:shadowView])) {
|
1262 | // Merging already registered changed props and new ones.
|
1263 | NSMutableSet *set = [NSMutableSet setWithArray:previousProps];
|
1264 | [set addObjectsFromArray:props];
|
1265 | props = [set allObjects];
|
1266 | }
|
1267 |
|
1268 | [_shadowViewsWithUpdatedProps setObject:props forKey:shadowView];
|
1269 | }
|
1270 |
|
1271 | - (void)_shadowViewDidReceiveUpdatedChildren:(RCTShadowView *)shadowView
|
1272 | {
|
1273 | [_shadowViewsWithUpdatedChildren addObject:shadowView];
|
1274 | }
|
1275 |
|
1276 | - (void)_dispatchChildrenDidChangeEvents
|
1277 | {
|
1278 | if (_shadowViewsWithUpdatedChildren.count == 0) {
|
1279 | return;
|
1280 | }
|
1281 |
|
1282 | NSHashTable<RCTShadowView *> *shadowViews = _shadowViewsWithUpdatedChildren;
|
1283 | _shadowViewsWithUpdatedChildren = [NSHashTable weakObjectsHashTable];
|
1284 |
|
1285 | NSMutableArray *tags = [NSMutableArray arrayWithCapacity:shadowViews.count];
|
1286 |
|
1287 | for (RCTShadowView *shadowView in shadowViews) {
|
1288 | [shadowView didUpdateReactSubviews];
|
1289 | [tags addObject:shadowView.reactTag];
|
1290 | }
|
1291 |
|
1292 | [self addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTUIView *> *viewRegistry) { // TODO(macOS ISS#3536887)
|
1293 | for (NSNumber *tag in tags) {
|
1294 | RCTUIView<RCTComponent> *view = viewRegistry[tag]; // TODO(macOS ISS#3536887)
|
1295 | [view didUpdateReactSubviews];
|
1296 | }
|
1297 | }];
|
1298 | }
|
1299 |
|
1300 | - (void)_dispatchPropsDidChangeEvents
|
1301 | {
|
1302 | if (_shadowViewsWithUpdatedProps.count == 0) {
|
1303 | return;
|
1304 | }
|
1305 |
|
1306 | NSMapTable<RCTShadowView *, NSArray<NSString *> *> *shadowViews = _shadowViewsWithUpdatedProps;
|
1307 | _shadowViewsWithUpdatedProps = [NSMapTable weakToStrongObjectsMapTable];
|
1308 |
|
1309 | NSMapTable<NSNumber *, NSArray<NSString *> *> *tags = [NSMapTable strongToStrongObjectsMapTable];
|
1310 |
|
1311 | for (RCTShadowView *shadowView in shadowViews) {
|
1312 | NSArray<NSString *> *props = [shadowViews objectForKey:shadowView];
|
1313 | [shadowView didSetProps:props];
|
1314 | [tags setObject:props forKey:shadowView.reactTag];
|
1315 | }
|
1316 |
|
1317 | [self addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTUIView *> *viewRegistry) { // TODO(macOS ISS#3536887)
|
1318 | for (NSNumber *tag in tags) {
|
1319 | RCTUIView<RCTComponent> *view = viewRegistry[tag]; // TODO(macOS ISS#3536887)
|
1320 | [view didSetProps:[tags objectForKey:tag]];
|
1321 | }
|
1322 | }];
|
1323 | }
|
1324 |
|
1325 | RCT_EXPORT_METHOD(measure : (nonnull NSNumber *)reactTag callback : (RCTResponseSenderBlock)callback)
|
1326 | {
|
1327 | [self addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTPlatformView *> *viewRegistry) { // TODO(macOS ISS#2323203)
|
1328 | RCTPlatformView *view = viewRegistry[reactTag]; // TODO(macOS ISS#2323203)
|
1329 | if (!view) {
|
1330 | // this view was probably collapsed out
|
1331 | RCTLogWarn(@"measure cannot find view with tag #%@", reactTag);
|
1332 | callback(@[]);
|
1333 | return;
|
1334 | }
|
1335 |
|
1336 | // If in a <Modal>, rootView will be the root of the modal container.
|
1337 | RCTPlatformView *rootView = view; // TODO(macOS ISS#2323203)
|
1338 | while (rootView.superview && ![rootView isReactRootView]) {
|
1339 | rootView = rootView.superview;
|
1340 | }
|
1341 |
|
1342 | // By convention, all coordinates, whether they be touch coordinates, or
|
1343 | // measurement coordinates are with respect to the root view.
|
1344 | CGRect frame = view.frame;
|
1345 | CGRect globalBounds = [view convertRect:view.bounds toView:rootView];
|
1346 |
|
1347 | callback(@[
|
1348 | @(frame.origin.x),
|
1349 | @(frame.origin.y),
|
1350 | @(globalBounds.size.width),
|
1351 | @(globalBounds.size.height),
|
1352 | @(globalBounds.origin.x),
|
1353 | @(globalBounds.origin.y),
|
1354 | ]);
|
1355 | }];
|
1356 | }
|
1357 |
|
1358 | RCT_EXPORT_METHOD(measureInWindow : (nonnull NSNumber *)reactTag callback : (RCTResponseSenderBlock)callback)
|
1359 | {
|
1360 | [self addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTPlatformView *> *viewRegistry) { // TODO(macOS ISS#2323203)
|
1361 | RCTPlatformView *view = viewRegistry[reactTag]; // TODO(macOS ISS#2323203)
|
1362 | if (!view) {
|
1363 | // this view was probably collapsed out
|
1364 | RCTLogWarn(@"measure cannot find view with tag #%@", reactTag);
|
1365 | callback(@[]);
|
1366 | return;
|
1367 | }
|
1368 |
|
1369 | // Return frame coordinates in window
|
1370 | CGRect windowFrame = [view convertRect:view.bounds toView:nil];
|
1371 | #if TARGET_OS_OSX // [TODO(macOS ISS#2323203)
|
1372 | //The macOS default coordinate system has its origin at the lower left of the drawing area, so we need to flip the y-axis coordinate.
|
1373 | windowFrame.origin.y = view.window.contentView.frame.size.height - windowFrame.origin.y - windowFrame.size.height;
|
1374 | #endif // ]TODO(macOS ISS#2323203)
|
1375 |
|
1376 | callback(@[
|
1377 | @(windowFrame.origin.x),
|
1378 | @(windowFrame.origin.y),
|
1379 | @(windowFrame.size.width),
|
1380 | @(windowFrame.size.height),
|
1381 | ]);
|
1382 | }];
|
1383 | }
|
1384 |
|
1385 |
|
1386 |
|
1387 |
|
1388 |
|
1389 | RCT_EXPORT_METHOD(viewIsDescendantOf
|
1390 | : (nonnull NSNumber *)reactTag ancestor
|
1391 | : (nonnull NSNumber *)ancestorReactTag callback
|
1392 | : (RCTResponseSenderBlock)callback)
|
1393 | {
|
1394 | RCTShadowView *shadowView = _shadowViewRegistry[reactTag];
|
1395 | RCTShadowView *ancestorShadowView = _shadowViewRegistry[ancestorReactTag];
|
1396 | if (!shadowView) {
|
1397 | return;
|
1398 | }
|
1399 | if (!ancestorShadowView) {
|
1400 | return;
|
1401 | }
|
1402 | BOOL viewIsAncestor = [shadowView viewIsDescendantOf:ancestorShadowView];
|
1403 | callback(@[ @(viewIsAncestor) ]);
|
1404 | }
|
1405 |
|
1406 | static void RCTMeasureLayout(RCTShadowView *view, RCTShadowView *ancestor, RCTResponseSenderBlock callback)
|
1407 | {
|
1408 | if (!view) {
|
1409 | return;
|
1410 | }
|
1411 | if (!ancestor) {
|
1412 | return;
|
1413 | }
|
1414 | CGRect result = [view measureLayoutRelativeToAncestor:ancestor];
|
1415 | if (CGRectIsNull(result)) {
|
1416 | RCTLogError(
|
1417 | @"view %@ (tag #%@) is not a descendant of %@ (tag #%@)", view, view.reactTag, ancestor, ancestor.reactTag);
|
1418 | return;
|
1419 | }
|
1420 | CGFloat leftOffset = result.origin.x;
|
1421 | CGFloat topOffset = result.origin.y;
|
1422 | CGFloat width = result.size.width;
|
1423 | CGFloat height = result.size.height;
|
1424 | if (isnan(leftOffset) || isnan(topOffset) || isnan(width) || isnan(height)) {
|
1425 | RCTLogError(@"Attempted to measure layout but offset or dimensions were NaN");
|
1426 | return;
|
1427 | }
|
1428 | callback(@[ @(leftOffset), @(topOffset), @(width), @(height) ]);
|
1429 | }
|
1430 |
|
1431 |
|
1432 |
|
1433 |
|
1434 |
|
1435 |
|
1436 |
|
1437 |
|
1438 | RCT_EXPORT_METHOD(measureLayout
|
1439 | : (nonnull NSNumber *)reactTag relativeTo
|
1440 | : (nonnull NSNumber *)ancestorReactTag errorCallback
|
1441 | : (__unused RCTResponseSenderBlock)errorCallback callback
|
1442 | : (RCTResponseSenderBlock)callback)
|
1443 | {
|
1444 | RCTShadowView *shadowView = _shadowViewRegistry[reactTag];
|
1445 | RCTShadowView *ancestorShadowView = _shadowViewRegistry[ancestorReactTag];
|
1446 | RCTMeasureLayout(shadowView, ancestorShadowView, callback);
|
1447 | }
|
1448 |
|
1449 |
|
1450 |
|
1451 |
|
1452 |
|
1453 |
|
1454 |
|
1455 |
|
1456 | RCT_EXPORT_METHOD(measureLayoutRelativeToParent
|
1457 | : (nonnull NSNumber *)reactTag errorCallback
|
1458 | : (__unused RCTResponseSenderBlock)errorCallback callback
|
1459 | : (RCTResponseSenderBlock)callback)
|
1460 | {
|
1461 | RCTLogWarn(
|
1462 | @"RCTUIManager.measureLayoutRelativeToParent method is deprecated and it will not be implemented in newer versions of RN (Fabric) - T47686450");
|
1463 | RCTShadowView *shadowView = _shadowViewRegistry[reactTag];
|
1464 | RCTMeasureLayout(shadowView, shadowView.reactSuperview, callback);
|
1465 | }
|
1466 |
|
1467 |
|
1468 |
|
1469 |
|
1470 |
|
1471 | RCT_EXPORT_METHOD(setJSResponder
|
1472 | : (nonnull NSNumber *)reactTag blockNativeResponder
|
1473 | : (__unused BOOL)blockNativeResponder)
|
1474 | {
|
1475 | [self addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTPlatformView *> *viewRegistry) { // TODO(macOS ISS#2323203)
|
1476 | _jsResponder = viewRegistry[reactTag];
|
1477 | if (!_jsResponder) {
|
1478 | RCTLogWarn(@"Invalid view set to be the JS responder - tag %@", reactTag);
|
1479 | }
|
1480 | }];
|
1481 | }
|
1482 |
|
1483 | RCT_EXPORT_METHOD(clearJSResponder)
|
1484 | {
|
1485 | [self addUIBlock:^(__unused RCTUIManager *uiManager, __unused NSDictionary<NSNumber *, RCTPlatformView *> *viewRegistry) { // TODO(macOS ISS#2323203)
|
1486 | _jsResponder = nil;
|
1487 | }];
|
1488 | }
|
1489 |
|
1490 | static NSMutableDictionary<NSString *, id> *moduleConstantsForComponent(
|
1491 | NSMutableDictionary<NSString *, NSDictionary *> *directEvents,
|
1492 | NSMutableDictionary<NSString *, NSDictionary *> *bubblingEvents,
|
1493 | RCTComponentData *componentData)
|
1494 | {
|
1495 | NSMutableDictionary<NSString *, id> *moduleConstants = [NSMutableDictionary new];
|
1496 |
|
1497 | // Register which event-types this view dispatches.
|
1498 | // React needs this for the event plugin.
|
1499 | NSMutableDictionary<NSString *, NSDictionary *> *bubblingEventTypes = [NSMutableDictionary new];
|
1500 | NSMutableDictionary<NSString *, NSDictionary *> *directEventTypes = [NSMutableDictionary new];
|
1501 |
|
1502 | // Add manager class
|
1503 | moduleConstants[@"Manager"] = RCTBridgeModuleNameForClass(componentData.managerClass);
|
1504 |
|
1505 | // Add native props
|
1506 | NSDictionary<NSString *, id> *viewConfig = [componentData viewConfig];
|
1507 | moduleConstants[@"NativeProps"] = viewConfig[@"propTypes"];
|
1508 | moduleConstants[@"baseModuleName"] = viewConfig[@"baseModuleName"];
|
1509 | moduleConstants[@"bubblingEventTypes"] = bubblingEventTypes;
|
1510 | moduleConstants[@"directEventTypes"] = directEventTypes;
|
1511 |
|
1512 | // Add direct events
|
1513 | for (NSString *eventName in viewConfig[@"directEvents"]) {
|
1514 | if (!directEvents[eventName]) {
|
1515 | directEvents[eventName] = @{
|
1516 | @"registrationName" : [eventName stringByReplacingCharactersInRange:(NSRange){0, 3} withString:@"on"],
|
1517 | };
|
1518 | }
|
1519 | directEventTypes[eventName] = directEvents[eventName];
|
1520 | if (RCT_DEBUG && bubblingEvents[eventName]) {
|
1521 | RCTLogError(
|
1522 | @"Component '%@' re-registered bubbling event '%@' as a "
|
1523 | "direct event",
|
1524 | componentData.name,
|
1525 | eventName);
|
1526 | }
|
1527 | }
|
1528 |
|
1529 | // Add bubbling events
|
1530 | for (NSString *eventName in viewConfig[@"bubblingEvents"]) {
|
1531 | if (!bubblingEvents[eventName]) {
|
1532 | NSString *bubbleName = [eventName stringByReplacingCharactersInRange:(NSRange){0, 3} withString:@"on"];
|
1533 | bubblingEvents[eventName] = @{
|
1534 | @"phasedRegistrationNames" : @{
|
1535 | @"bubbled" : bubbleName,
|
1536 | @"captured" : [bubbleName stringByAppendingString:@"Capture"],
|
1537 | }
|
1538 | };
|
1539 | }
|
1540 | bubblingEventTypes[eventName] = bubblingEvents[eventName];
|
1541 | if (RCT_DEBUG && directEvents[eventName]) {
|
1542 | RCTLogError(
|
1543 | @"Component '%@' re-registered direct event '%@' as a "
|
1544 | "bubbling event",
|
1545 | componentData.name,
|
1546 | eventName);
|
1547 | }
|
1548 | }
|
1549 |
|
1550 | return moduleConstants;
|
1551 | }
|
1552 |
|
1553 | - (NSDictionary<NSString *, id> *)constantsToExport
|
1554 | {
|
1555 | return [self getConstants];
|
1556 | }
|
1557 |
|
1558 | - (NSDictionary<NSString *, id> *)getConstants
|
1559 | {
|
1560 | NSMutableDictionary<NSString *, NSDictionary *> *constants = [NSMutableDictionary new];
|
1561 | NSMutableDictionary<NSString *, NSDictionary *> *directEvents = [NSMutableDictionary new];
|
1562 | NSMutableDictionary<NSString *, NSDictionary *> *bubblingEvents = [NSMutableDictionary new];
|
1563 |
|
1564 | [_componentDataByName
|
1565 | enumerateKeysAndObjectsUsingBlock:^(NSString *name, RCTComponentData *componentData, __unused BOOL *stop) {
|
1566 | RCTAssert(!constants[name], @"UIManager already has constants for %@", componentData.name);
|
1567 | NSMutableDictionary<NSString *, id> *moduleConstants =
|
1568 | moduleConstantsForComponent(directEvents, bubblingEvents, componentData);
|
1569 | constants[name] = moduleConstants;
|
1570 | }];
|
1571 |
|
1572 | return constants;
|
1573 | }
|
1574 |
|
1575 | RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(lazilyLoadView : (NSString *)name)
|
1576 | {
|
1577 | if (_componentDataByName[name]) {
|
1578 | return @{};
|
1579 | }
|
1580 |
|
1581 | id<RCTBridgeDelegate> delegate = self.bridge.delegate;
|
1582 | if (![delegate respondsToSelector:@selector(bridge:didNotFindModule:)]) {
|
1583 | return @{};
|
1584 | }
|
1585 |
|
1586 | NSString *moduleName = name;
|
1587 | BOOL result = [delegate bridge:self.bridge didNotFindModule:moduleName];
|
1588 | if (!result) {
|
1589 | moduleName = [name stringByAppendingString:@"Manager"];
|
1590 | result = [delegate bridge:self.bridge didNotFindModule:moduleName];
|
1591 | }
|
1592 | if (!result) {
|
1593 | return @{};
|
1594 | }
|
1595 |
|
1596 | id module = [self.bridge moduleForName:moduleName lazilyLoadIfNecessary:RCTTurboModuleEnabled()];
|
1597 | if (module == nil) {
|
1598 | // There is all sorts of code in this codebase that drops prefixes.
|
1599 | //
|
1600 | // If we didn't find a module, it's possible because it's stored under a key
|
1601 | // which had RCT Prefixes stripped. Lets check one more time...
|
1602 | module = [self.bridge moduleForName:RCTDropReactPrefixes(moduleName) lazilyLoadIfNecessary:RCTTurboModuleEnabled()];
|
1603 | }
|
1604 |
|
1605 | if (!module) {
|
1606 | return @{};
|
1607 | }
|
1608 |
|
1609 | RCTComponentData *componentData = [[RCTComponentData alloc] initWithManagerClass:[module class] bridge:self.bridge];
|
1610 | _componentDataByName[componentData.name] = componentData;
|
1611 | NSMutableDictionary *directEvents = [NSMutableDictionary new];
|
1612 | NSMutableDictionary *bubblingEvents = [NSMutableDictionary new];
|
1613 | NSMutableDictionary<NSString *, id> *moduleConstants =
|
1614 | moduleConstantsForComponent(directEvents, bubblingEvents, componentData);
|
1615 | return @{
|
1616 | @"viewConfig" : moduleConstants,
|
1617 | };
|
1618 | }
|
1619 |
|
1620 | RCT_EXPORT_METHOD(configureNextLayoutAnimation
|
1621 | : (NSDictionary *)config withCallback
|
1622 | : (RCTResponseSenderBlock)callback errorCallback
|
1623 | : (__unused RCTResponseSenderBlock)errorCallback)
|
1624 | {
|
1625 | RCTLayoutAnimationGroup *layoutAnimationGroup = [[RCTLayoutAnimationGroup alloc] initWithConfig:config
|
1626 | callback:callback];
|
1627 |
|
1628 | [self addUIBlock:^(RCTUIManager *uiManager, __unused NSDictionary<NSNumber *, RCTPlatformView *> *viewRegistry) { // TODO(macOS ISS#2323203)
|
1629 | [uiManager setNextLayoutAnimationGroup:layoutAnimationGroup];
|
1630 | }];
|
1631 | }
|
1632 |
|
1633 | - (void)rootViewForReactTag:(NSNumber *)reactTag withCompletion:(void (^)(RCTPlatformView *view))completion // TODO(macOS ISS#2323203)
|
1634 | {
|
1635 | RCTAssertMainQueue();
|
1636 | RCTAssert(completion != nil, @"Attempted to resolve rootView for tag %@ without a completion block", reactTag);
|
1637 |
|
1638 | if (reactTag == nil) {
|
1639 | completion(nil);
|
1640 | return;
|
1641 | }
|
1642 |
|
1643 | RCTExecuteOnUIManagerQueue(^{
|
1644 | NSNumber *rootTag = [self shadowViewForReactTag:reactTag].rootView.reactTag;
|
1645 | RCTExecuteOnMainQueue(^{
|
1646 | RCTPlatformView *rootView = nil; // TODO(macOS ISS#2323203)
|
1647 | if (rootTag != nil) {
|
1648 | rootView = [self viewForReactTag:rootTag];
|
1649 | }
|
1650 | completion(rootView);
|
1651 | });
|
1652 | });
|
1653 | }
|
1654 |
|
1655 |
|
1656 | static RCTPlatformView *_jsResponder; // TODO(macOS ISS#2323203)
|
1657 |
|
1658 | + (RCTPlatformView *)JSResponder // TODO(macOS ISS#2323203)
|
1659 | {
|
1660 | return _jsResponder;
|
1661 | }
|
1662 |
|
1663 | @end
|
1664 |
|
1665 | @implementation RCTBridge (RCTUIManager)
|
1666 |
|
1667 | - (RCTUIManager *)uiManager
|
1668 | {
|
1669 | return [self moduleForClass:[RCTUIManager class]];
|
1670 | }
|
1671 |
|
1672 | @end
|