From 35e7a266db1927d6b3efd5e8b43394518ec86f59 Mon Sep 17 00:00:00 2001 From: Kevin Gozali Date: Sun, 28 Aug 2016 22:46:42 -0700 Subject: [PATCH] allow finding the rootTag of any reactTag inside UIManager Summary: added API to in UIManager to find the rootTag/View of any reactTag based on its layout (shadow views) hierarchy (not to be used by JS) Reviewed By: javache Differential Revision: D3750410 fbshipit-source-id: 68611e39930d53ece478f25245ddc7f7838daaa6 --- React/Modules/RCTUIManager.h | 11 +++++ React/Modules/RCTUIManager.m | 48 +++++++++++++++++++ .../react/uimanager/UIImplementation.java | 21 +++++++- .../react/uimanager/UIManagerModule.java | 13 +++++ 4 files changed, 92 insertions(+), 1 deletion(-) diff --git a/React/Modules/RCTUIManager.h b/React/Modules/RCTUIManager.h index 30823ac85..afd276167 100644 --- a/React/Modules/RCTUIManager.h +++ b/React/Modules/RCTUIManager.h @@ -90,6 +90,17 @@ RCT_EXTERN NSString *const RCTUIManagerRootViewKey; */ - (void)addUIBlock:(RCTViewManagerUIBlock)block; +/** + * Given a reactTag from a component, find its root view, if possible. + * Otherwise, this will give back nil. + * + * @param reactTag the component tag + * @param completion the completion block that will hand over the rootView, if any. + * + * @return the rootView + */ +- (void)rootViewForReactTag:(NSNumber *)reactTag withCompletion:(void (^)(UIView *view))completion; + /** * The view that is currently first responder, according to the JS context. */ diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index 264c01950..a5915872b 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -1549,6 +1549,54 @@ RCT_EXPORT_METHOD(configureNextLayoutAnimation:(NSDictionary *)config }]; } +- (void)rootViewForReactTag:(NSNumber *)reactTag withCompletion:(void (^)(UIView *view))completion +{ + RCTAssertMainQueue(); + RCTAssert(completion != nil, @"Attempted to resolve rootView for tag %@ without a completion block", reactTag); + + if (reactTag == nil) { + completion(nil); + return; + } + + dispatch_async(RCTGetUIManagerQueue(), ^{ + NSNumber *rootTag = [self _rootTagForReactTag:reactTag]; + dispatch_async(dispatch_get_main_queue(), ^{ + UIView *rootView = nil; + if (rootTag != nil) { + rootView = [self viewForReactTag:rootTag]; + } + completion(rootView); + }); + }); +} + +- (NSNumber *)_rootTagForReactTag:(NSNumber *)reactTag +{ + RCTAssert(!RCTIsMainQueue(), @"Should be called on shadow queue"); + + if (reactTag == nil) { + return nil; + } + + if (RCTIsReactRootView(reactTag)) { + return reactTag; + } + + NSNumber *rootTag = nil; + RCTShadowView *shadowView = _shadowViewRegistry[reactTag]; + while (shadowView) { + RCTShadowView *parent = [shadowView reactSuperview]; + if (!parent && RCTIsReactRootView(shadowView.reactTag)) { + rootTag = shadowView.reactTag; + break; + } + shadowView = parent; + } + + return rootTag; +} + static UIView *_jsResponder; + (UIView *)JSResponder diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java index 729077313..18600f3c4 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java @@ -13,6 +13,7 @@ import javax.annotation.Nullable; import java.util.Arrays; import java.util.List; +import com.facebook.common.logging.FLog; import com.facebook.csslayout.CSSLayoutContext; import com.facebook.csslayout.CSSDirection; import com.facebook.infer.annotation.Assertions; @@ -24,6 +25,7 @@ import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.bridge.WritableArray; +import com.facebook.react.common.ReactConstants; import com.facebook.react.modules.i18nmanager.I18nUtil; import com.facebook.react.uimanager.debug.NotThreadSafeViewHierarchyUpdateDebugListener; import com.facebook.react.uimanager.events.EventDispatcher; @@ -575,7 +577,6 @@ public class UIImplementation { mOperationsQueue.enqueueConfigureLayoutAnimation(config, success, error); } - public void setJSResponder(int reactTag, boolean blockNativeResponder) { assertViewExists(reactTag, "setJSResponder"); ReactShadowNode node = mShadowNodeRegistry.getNode(reactTag); @@ -789,4 +790,22 @@ public class UIImplementation { public void addUIBlock(UIBlock block) { mOperationsQueue.enqueueUIBlock(block); } + + public int resolveRootTagFromReactTag(int reactTag) { + if (mShadowNodeRegistry.isRootNode(reactTag)) { + return reactTag; + } + + ReactShadowNode node = resolveShadowNode(reactTag); + int rootTag = 0; + if (node != null) { + rootTag = node.getRootNode().getReactTag(); + } else { + FLog.w( + ReactConstants.TAG, + "Warning : attempted to resolve a non-existent react shadow node. reactTag=" + reactTag); + } + + return rootTag; + } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java index 033ea83e0..ce32ea442 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java @@ -518,4 +518,17 @@ public class UIManagerModule extends ReactContextBaseJavaModule implements public void addUIBlock (UIBlock block) { mUIImplementation.addUIBlock(block); } + + /** + * Given a reactTag from a component, find its root node tag, if possible. + * Otherwise, this will return 0. If the reactTag belongs to a root node, this + * will return the same reactTag. + * + * @param reactTag the component tag + * + * @return the rootTag + */ + public int resolveRootTagFromReactTag(int reactTag) { + return mUIImplementation.resolveRootTagFromReactTag(reactTag); + } }