From 46803f0617bb933b9a626ff8972ea0ef80b9415a Mon Sep 17 00:00:00 2001 From: Pawel Sienkowski Date: Mon, 26 Oct 2015 15:39:04 -0700 Subject: [PATCH] Size flexibility modes for RCTRootView Reviewed By: javache Differential Revision: D2526355 fb-gh-sync-id: 095a43bc01f883fdfdad3a086a35682c20c05597 --- Libraries/ReactIOS/renderApplication.ios.js | 6 +---- React/Base/RCTRootView.h | 19 ++++++++++++++ React/Base/RCTRootView.m | 7 ++++++ React/Modules/RCTUIManager.m | 20 ++++++++++++--- React/Views/RCTShadowView.h | 10 ++++++-- React/Views/RCTShadowView.m | 28 +++++++++++++++++++-- 6 files changed, 78 insertions(+), 12 deletions(-) diff --git a/Libraries/ReactIOS/renderApplication.ios.js b/Libraries/ReactIOS/renderApplication.ios.js index 6a4bb5f68..8a0c8959b 100644 --- a/Libraries/ReactIOS/renderApplication.ios.js +++ b/Libraries/ReactIOS/renderApplication.ios.js @@ -87,11 +87,7 @@ function renderApplication( var styles = StyleSheet.create({ appContainer: { - position: 'absolute', - left: 0, - top: 0, - right: 0, - bottom: 0, + flex: 1, }, }); diff --git a/React/Base/RCTRootView.h b/React/Base/RCTRootView.h index 80f86d0cb..7e799ae69 100644 --- a/React/Base/RCTRootView.h +++ b/React/Base/RCTRootView.h @@ -11,6 +11,20 @@ #import "RCTBridge.h" +/** + * This enum is used to define size flexibility type of the root view. + * If a dimension is flexible, the view will recalculate that dimension + * so the content fits. Recalculations are performed when the root's frame, + * size flexibility mode or content size changes. After a recalculation, + * TODO: will be called with the new size passed as an argument. + */ +typedef NS_ENUM(NSInteger, RCTRootViewSizeFlexibility) { + RCTRootViewSizeFlexibilityNone = 0, + RCTRootViewSizeFlexibilityWidth, + RCTRootViewSizeFlexibilityHeight, + RCTRootViewSizeFlexibilityWidthAndHeight, +}; + /** * This notification is sent when the first subviews are added to the root view * after the application has loaded. This is used to hide the `loadingView`, and @@ -71,6 +85,11 @@ extern NSString *const RCTContentDidAppearNotification; */ @property (nonatomic, strong) Class executorClass; +/** + * The size flexibility mode of the root view. + */ +@property (nonatomic, assign) RCTRootViewSizeFlexibility sizeFlexibility; + /** * The backing view controller of the root view. */ diff --git a/React/Base/RCTRootView.m b/React/Base/RCTRootView.m index ca2f75e3f..6705c81aa 100644 --- a/React/Base/RCTRootView.m +++ b/React/Base/RCTRootView.m @@ -72,6 +72,7 @@ NSString *const RCTContentDidAppearNotification = @"RCTContentDidAppearNotificat _initialProperties = [initialProperties copy]; _loadingViewFadeDelay = 0.25; _loadingViewFadeDuration = 0.25; + _sizeFlexibility = RCTRootViewSizeFlexibilityNone; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(javaScriptDidLoad:) @@ -189,6 +190,12 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) args:@[moduleName, appParameters]]; } +- (void)setSizeFlexibility:(RCTRootViewSizeFlexibility)sizeFlexibility +{ + _sizeFlexibility = sizeFlexibility; + [self setNeedsLayout]; +} + - (void)layoutSubviews { [super layoutSubviews]; diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index 87ae23d80..49e3bc7a3 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -347,11 +347,26 @@ extern NSString *RCTBridgeModuleNameForClass(Class cls); { RCTAssertMainThread(); + // The following variables have no meaning if the view is not a react root view + RCTRootView *rootView = (RCTRootView *)[view superview]; + RCTRootViewSizeFlexibility sizeFlexibility = rootView != nil ? rootView.sizeFlexibility : RCTRootViewSizeFlexibilityNone; + NSNumber *reactTag = view.reactTag; dispatch_async(_shadowQueue, ^{ RCTShadowView *rootShadowView = _shadowViewRegistry[reactTag]; RCTAssert(rootShadowView != nil, @"Could not locate root view with tag #%@", reactTag); - rootShadowView.frame = frame; + + if (RCTIsReactRootView(reactTag)) { + if (CGRectEqualToRect(frame, rootShadowView.frame) && rootShadowView.sizeFlexibility == sizeFlexibility) { + // This is to prevent infinite recursion when the frame update is trigerred by TODO(8608567): + return; + } + rootShadowView.frame = frame; + rootShadowView.sizeFlexibility = sizeFlexibility; + } else { + rootShadowView.frame = frame; + } + [rootShadowView updateLayout]; [self batchDidComplete]; @@ -437,8 +452,7 @@ extern NSString *RCTBridgeModuleNameForClass(Class cls); // these structures in the UI-thread block. `NSMutableArray` is not thread // safe so we rely on the fact that we never mutate it after it's passed to // the main thread. - [rootShadowView collectRootUpdatedFrames:viewsWithNewFrames - parentConstraint:(CGSize){CSS_UNDEFINED, CSS_UNDEFINED}]; + [rootShadowView collectRootUpdatedFrames:viewsWithNewFrames]; // Parallel arrays are built and then handed off to main thread NSMutableArray *frameReactTags = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count]; diff --git a/React/Views/RCTShadowView.h b/React/Views/RCTShadowView.h index 9aa7f0299..8461c818d 100644 --- a/React/Views/RCTShadowView.h +++ b/React/Views/RCTShadowView.h @@ -11,6 +11,7 @@ #import "Layout.h" #import "RCTComponent.h" +#import "RCTRootView.h" @class RCTSparseArray; @@ -64,6 +65,12 @@ typedef void (^RCTApplierBlock)(RCTSparseArray *viewRegistry); - (void)setTopLeft:(CGPoint)topLeft; - (void)setSize:(CGSize)size; +/** + * Size flexibility type used to find size constraints. + * Default to RCTRootViewSizeFlexibilityNone + */ +@property (nonatomic, assign) RCTRootViewSizeFlexibility sizeFlexibility; + /** * Border. Defaults to { 0, 0, 0, 0 }. */ @@ -127,8 +134,7 @@ typedef void (^RCTApplierBlock)(RCTSparseArray *viewRegistry); * Calculate all views whose frame needs updating after layout has been calculated. * The viewsWithNewFrame set contains the reactTags of the views that need updating. */ -- (void)collectRootUpdatedFrames:(NSMutableSet *)viewsWithNewFrame - parentConstraint:(CGSize)parentConstraint; +- (void)collectRootUpdatedFrames:(NSMutableSet *)viewsWithNewFrame; /** * Recursively apply layout to children. diff --git a/React/Views/RCTShadowView.m b/React/Views/RCTShadowView.m index f1b942b2d..2161a3763 100644 --- a/React/Views/RCTShadowView.m +++ b/React/Views/RCTShadowView.m @@ -215,9 +215,32 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st } } -- (void)collectRootUpdatedFrames:(NSMutableSet *)viewsWithNewFrame - parentConstraint:(__unused CGSize)parentConstraint + +- (void)applySizeConstraints { + switch (_sizeFlexibility) { + case RCTRootViewSizeFlexibilityNone: + break; + case RCTRootViewSizeFlexibilityWidth: + _cssNode->style.dimensions[CSS_WIDTH] = CSS_UNDEFINED; + break; + case RCTRootViewSizeFlexibilityHeight: + _cssNode->style.dimensions[CSS_HEIGHT] = CSS_UNDEFINED; + break; + case RCTRootViewSizeFlexibilityWidthAndHeight: + _cssNode->style.dimensions[CSS_WIDTH] = CSS_UNDEFINED; + _cssNode->style.dimensions[CSS_HEIGHT] = CSS_UNDEFINED; + break; + } +} + +- (void)collectRootUpdatedFrames:(NSMutableSet *)viewsWithNewFrame +{ + RCTAssert(RCTIsReactRootView(self.reactTag), + @"The method has been called on a view with react tag %@, which is not a root view", self.reactTag); + + [self applySizeConstraints]; + [self fillCSSNode:_cssNode]; layoutNode(_cssNode, CSS_UNDEFINED, CSS_DIRECTION_INHERIT); [self applyLayoutNode:_cssNode viewsWithNewFrame:viewsWithNewFrame absolutePosition:CGPointZero]; @@ -245,6 +268,7 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st if ((self = [super init])) { _frame = CGRectMake(0, 0, CSS_UNDEFINED, CSS_UNDEFINED); + _sizeFlexibility = RCTRootViewSizeFlexibilityNone; for (unsigned int ii = 0; ii < META_PROP_COUNT; ii++) { _paddingMetaProps[ii] = CSS_UNDEFINED;