RCTUIManager.m 57 KB

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