Extracted rootview-specific shadowview logic into new class

Summary:It was hard to understand which parts of the shadowview API are designed to be called only on the root view, and which were applicable to any view.

This diff extracts rootview-specific logic out into a new RCTRootShadowView class.

Reviewed By: majak

Differential Revision: D3063905

fb-gh-sync-id: ef890cddfd7625fbd4bf5454314b441acdb03ac8
shipit-source-id: ef890cddfd7625fbd4bf5454314b441acdb03ac8
This commit is contained in:
Nick Lockwood 2016-03-21 03:20:49 -07:00 committed by Facebook Github Bot 9
parent 7d059a12f3
commit d033c45f93
12 changed files with 131 additions and 85 deletions

View File

@ -15,9 +15,11 @@
#import <XCTest/XCTest.h>
#import "RCTShadowView.h"
#import "RCTRootShadowView.h"
@interface RCTShadowViewTests : XCTestCase
@property (nonatomic, strong) RCTShadowView *parentView;
@property (nonatomic, strong) RCTRootShadowView *parentView;
@end
@implementation RCTShadowViewTests
@ -25,7 +27,7 @@
- (void)setUp
{
[super setUp];
self.parentView = [self _shadowViewWithStyle:^(css_style_t *style) {
style->flex_direction = CSS_FLEX_DIRECTION_COLUMN;
style->dimensions[0] = 440;
@ -90,7 +92,7 @@
[self.parentView insertReactSubview:mainView atIndex:1];
[self.parentView insertReactSubview:footerView atIndex:2];
[self.parentView collectRootUpdatedFrames];
[self.parentView collectViewsWithUpdatedFrames];
XCTAssertTrue(CGRectEqualToRect([self.parentView measureLayoutRelativeToAncestor:self.parentView], CGRectMake(0, 0, 440, 440)));
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets([self.parentView paddingAsInsets], UIEdgeInsetsMake(10, 10, 10, 10)));
@ -156,7 +158,7 @@
RCTShadowView *view = [self _shadowViewWithStyle:styleBlock];
[self.parentView insertReactSubview:view atIndex:0];
view.intrinsicContentSize = contentSize;
[self.parentView collectRootUpdatedFrames];
[self.parentView collectViewsWithUpdatedFrames];
CGRect actualRect = [view measureLayoutRelativeToAncestor:self.parentView];
XCTAssertTrue(CGRectEqualToRect(expectedRect, actualRect),
@"Expected layout to be %@, got %@",
@ -164,9 +166,9 @@
NSStringFromCGRect(actualRect));
}
- (RCTShadowView *)_shadowViewWithStyle:(void(^)(css_style_t *style))styleBlock
- (RCTRootShadowView *)_shadowViewWithStyle:(void(^)(css_style_t *style))styleBlock
{
RCTShadowView *shadowView = [RCTShadowView new];
RCTRootShadowView *shadowView = [RCTRootShadowView new];
css_style_t style = shadowView.cssNode->style;
styleBlock(&style);

View File

@ -61,7 +61,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
magicNumber = NSSwapLittleIntToHost(magicNumber);
int32_t sourceLength = 0;
int64_t sourceLength = 0;
if (magicNumber == RCTRAMBundleMagicNumber) {
source = [NSData dataWithBytes:&magicNumber length:sizeof(magicNumber)];

View File

@ -15,7 +15,7 @@
#import "RCTProfile.h"
static int64_t RCTPLData[RCTPLSize][2] = {};
static int64_t RCTPLCookies[RCTPLSize] = {};
static NSUInteger RCTPLCookies[RCTPLSize] = {};
void RCTPerformanceLoggerStart(RCTPLTag tag)
{

View File

@ -359,7 +359,7 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder:(nonnull NSCoder *)aDecoder)
{
_backgroundColor = backgroundColor;
if (self.reactTag && _bridge.isValid) {
[_bridge.uiManager setBackgroundColor:backgroundColor forRootView:self];
[_bridge.uiManager setBackgroundColor:backgroundColor forView:self];
}
}

View File

@ -33,8 +33,7 @@ NSString *const RCTJavaScriptContextCreatedNotification = @"RCTJavaScriptContext
static NSString *const RCTJSCProfilerEnabledDefaultsKey = @"RCTJSCProfilerEnabled";
typedef struct ModuleData
{
typedef struct ModuleData {
uint32_t offset;
uint32_t length;
uint32_t lineNo;
@ -706,7 +705,9 @@ static int readBundle(FILE *fd, size_t offset, size_t length, void *ptr) {
{
_bundle = fopen(sourceURL.path.UTF8String, "r");
if (!_bundle) {
*error = RCTErrorWithMessage([NSString stringWithFormat:@"Bundle %@ cannot be opened: %d", sourceURL.path, errno]);
if (error) {
*error = RCTErrorWithMessage([NSString stringWithFormat:@"Bundle %@ cannot be opened: %d", sourceURL.path, errno]);
}
return nil;
}
@ -721,7 +722,9 @@ static int readBundle(FILE *fd, size_t offset, size_t length, void *ptr) {
uint32_t tableLength;
if (readBundle(_bundle, currentOffset, sizeof(tableLength), &tableLength) != 0) {
*error = RCTErrorWithMessage(@"Error loading RAM Bundle");
if (error) {
*error = RCTErrorWithMessage(@"Error loading RAM Bundle");
}
return nil;
}
tableLength = NSSwapLittleIntToHost(tableLength);
@ -733,7 +736,9 @@ static int readBundle(FILE *fd, size_t offset, size_t length, void *ptr) {
char tableStart[tableLength];
if (readBundle(_bundle, currentOffset, tableLength, tableStart) != 0) {
*error = RCTErrorWithMessage(@"Error loading RAM Bundle");
if (error) {
*error = RCTErrorWithMessage(@"Error loading RAM Bundle");
}
return nil;
}
@ -745,7 +750,9 @@ static int readBundle(FILE *fd, size_t offset, size_t length, void *ptr) {
char *name = malloc(nameLength + 1);
if (!name) {
*error = RCTErrorWithMessage(@"Error loading RAM Bundle");
if (error) {
*error = RCTErrorWithMessage(@"Error loading RAM Bundle");
}
return nil;
}
@ -767,12 +774,17 @@ static int readBundle(FILE *fd, size_t offset, size_t length, void *ptr) {
void *startupCode;
if (!(startupCode = malloc(startupData->length))) {
*error = RCTErrorWithMessage(@"Error loading RAM Bundle");
if (error) {
*error = RCTErrorWithMessage(@"Error loading RAM Bundle");
}
return nil;
}
if (readBundle(_bundle, startupData->offset, startupData->length, startupCode) != 0) {
*error = RCTErrorWithMessage(@"Error loading RAM Bundle");
if (error) {
*error = RCTErrorWithMessage(@"Error loading RAM Bundle");
}
free(startupCode);
return nil;
}
return [NSData dataWithBytesNoCopy:startupCode length:startupData->length freeWhenDone:YES];

View File

@ -67,10 +67,11 @@ RCT_EXTERN NSString *const RCTUIManagerRootViewKey;
- (void)setIntrinsicContentSize:(CGSize)size forView:(UIView *)view;
/**
* Update the background color of a root view. This is usually triggered by
* manually setting the background color of the root view with native code.
* Update the background color of a view. The source of truth for
* backgroundColor is the shadow view, so if to update backgroundColor from
* native code you will need to call this method.
*/
- (void)setBackgroundColor:(UIColor *)color forRootView:(UIView *)rootView;
- (void)setBackgroundColor:(UIColor *)color forView:(UIView *)view;
/**
* Schedule a block to be executed on the UI thread. Useful if you need to execute

View File

@ -27,6 +27,7 @@
#import "RCTModuleMethod.h"
#import "RCTProfile.h"
#import "RCTRootView.h"
#import "RCTRootShadowView.h"
#import "RCTRootViewInternal.h"
#import "RCTScrollableProtocol.h"
#import "RCTShadowView.h"
@ -358,7 +359,7 @@ RCT_EXPORT_MODULE()
if (!_viewRegistry) {
return;
}
RCTShadowView *shadowView = [RCTShadowView new];
RCTRootShadowView *shadowView = [RCTRootShadowView new];
shadowView.reactTag = reactTag;
shadowView.frame = frame;
shadowView.backgroundColor = rootView.backgroundColor;
@ -406,9 +407,12 @@ RCT_EXPORT_MODULE()
// Trigger re-layout when size flexibility changes, as the root view might grow or
// shrink in the flexible dimensions.
if (RCTIsReactRootView(reactTag) && shadowView.sizeFlexibility != sizeFlexibility) {
shadowView.sizeFlexibility = sizeFlexibility;
dirtyLayout = YES;
if (RCTIsReactRootView(reactTag)) {
RCTRootShadowView *rootShadowView = (RCTRootShadowView *)shadowView;
if (rootShadowView.sizeFlexibility != sizeFlexibility) {
rootShadowView.sizeFlexibility = sizeFlexibility;
dirtyLayout = YES;
}
}
if (dirtyLayout) {
@ -433,12 +437,11 @@ RCT_EXPORT_MODULE()
});
}
- (void)setBackgroundColor:(UIColor *)color forRootView:(UIView *)rootView
- (void)setBackgroundColor:(UIColor *)color forView:(UIView *)view
{
RCTAssertMainThread();
NSNumber *reactTag = rootView.reactTag;
RCTAssert(RCTIsReactRootView(reactTag), @"Specified view %@ is not a root view", reactTag);
NSNumber *reactTag = view.reactTag;
__weak RCTUIManager *weakSelf = self;
dispatch_async(_shadowQueue, ^{
@ -446,10 +449,10 @@ RCT_EXPORT_MODULE()
if (!_viewRegistry) {
return;
}
RCTShadowView *rootShadowView = strongSelf->_shadowViewRegistry[reactTag];
RCTAssert(rootShadowView != nil, @"Could not locate root view with tag #%@", reactTag);
rootShadowView.backgroundColor = color;
[self _amendPendingUIBlocksWithStylePropagationUpdateForRootView:rootShadowView];
RCTShadowView *shadowView = strongSelf->_shadowViewRegistry[reactTag];
RCTAssert(shadowView != nil, @"Could not locate root view with tag #%@", reactTag);
shadowView.backgroundColor = color;
[self _amendPendingUIBlocksWithStylePropagationUpdateForShadowView:shadowView];
[self flushUIBlocks];
});
}
@ -500,7 +503,7 @@ RCT_EXPORT_MODULE()
[_pendingUIBlocks addObject:outerBlock];
}
- (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTShadowView *)rootShadowView
- (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTRootShadowView *)rootShadowView
{
RCTAssert(![NSThread isMainThread], @"Should be called on shadow thread");
@ -509,7 +512,7 @@ RCT_EXPORT_MODULE()
// 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.
NSSet<RCTShadowView *> *viewsWithNewFrames = [rootShadowView collectRootUpdatedFrames];
NSSet<RCTShadowView *> *viewsWithNewFrames = [rootShadowView collectViewsWithUpdatedFrames];
if (!viewsWithNewFrames.count) {
// no frame change results in no UI update block
@ -656,7 +659,7 @@ RCT_EXPORT_MODULE()
};
}
- (void)_amendPendingUIBlocksWithStylePropagationUpdateForRootView:(RCTShadowView *)topView
- (void)_amendPendingUIBlocksWithStylePropagationUpdateForShadowView:(RCTShadowView *)topView
{
NSMutableSet<RCTApplierBlock> *applierBlocks = [NSMutableSet setWithCapacity:1];
[topView collectUpdatedProperties:applierBlocks parentProperties:@{}];
@ -1015,9 +1018,9 @@ RCT_EXPORT_METHOD(dispatchViewManagerCommand:(nonnull NSNumber *)reactTag
// Perform layout
for (NSNumber *reactTag in _rootViewTags) {
RCTShadowView *rootView = _shadowViewRegistry[reactTag];
RCTRootShadowView *rootView = (RCTRootShadowView *)_shadowViewRegistry[reactTag];
[self addUIBlock:[self uiBlockWithLayoutUpdateForRootView:rootView]];
[self _amendPendingUIBlocksWithStylePropagationUpdateForRootView:rootView];
[self _amendPendingUIBlocksWithStylePropagationUpdateForShadowView:rootView];
}
// Clear layout animations

View File

@ -41,6 +41,7 @@
13B080261A694A8400A75B9A /* RCTWrapperViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B080241A694A8400A75B9A /* RCTWrapperViewController.m */; };
13B202041BFB948C00C07393 /* RCTMapAnnotation.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B202031BFB948C00C07393 /* RCTMapAnnotation.m */; };
13BB3D021BECD54500932C10 /* RCTImageSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 13BB3D011BECD54500932C10 /* RCTImageSource.m */; };
13BCE8091C99CB9D00DD7AAD /* RCTRootShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 13BCE8081C99CB9D00DD7AAD /* RCTRootShadowView.m */; };
13C156051AB1A2840079392D /* RCTWebView.m in Sources */ = {isa = PBXBuildFile; fileRef = 13C156021AB1A2840079392D /* RCTWebView.m */; };
13C156061AB1A2840079392D /* RCTWebViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13C156041AB1A2840079392D /* RCTWebViewManager.m */; };
13CC8A821B17642100940AE7 /* RCTBorderDrawing.m in Sources */ = {isa = PBXBuildFile; fileRef = 13CC8A811B17642100940AE7 /* RCTBorderDrawing.m */; };
@ -186,6 +187,8 @@
13B202031BFB948C00C07393 /* RCTMapAnnotation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMapAnnotation.m; sourceTree = "<group>"; };
13BB3D001BECD54500932C10 /* RCTImageSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageSource.h; sourceTree = "<group>"; };
13BB3D011BECD54500932C10 /* RCTImageSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageSource.m; sourceTree = "<group>"; };
13BCE8071C99CB9D00DD7AAD /* RCTRootShadowView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTRootShadowView.h; sourceTree = "<group>"; };
13BCE8081C99CB9D00DD7AAD /* RCTRootShadowView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRootShadowView.m; sourceTree = "<group>"; };
13C156011AB1A2840079392D /* RCTWebView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebView.h; sourceTree = "<group>"; };
13C156021AB1A2840079392D /* RCTWebView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWebView.m; sourceTree = "<group>"; };
13C156031AB1A2840079392D /* RCTWebViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebViewManager.h; sourceTree = "<group>"; };
@ -424,6 +427,8 @@
131B6AF31AF1093D00FFC3E0 /* RCTSegmentedControlManager.m */,
13E0674B1A70F44B002CDEE1 /* RCTShadowView.h */,
13E0674C1A70F44B002CDEE1 /* RCTShadowView.m */,
13BCE8071C99CB9D00DD7AAD /* RCTRootShadowView.h */,
13BCE8081C99CB9D00DD7AAD /* RCTRootShadowView.m */,
13AF20431AE707F8005F5298 /* RCTSlider.h */,
13AF20441AE707F9005F5298 /* RCTSlider.m */,
14F484541AABFCE100FDF6B9 /* RCTSliderManager.h */,
@ -670,6 +675,7 @@
13B07FF01A69327A00A75B9A /* RCTExceptionsManager.m in Sources */,
13B202041BFB948C00C07393 /* RCTMapAnnotation.m in Sources */,
13A0C28A1B74F71200B29F6F /* RCTDevMenu.m in Sources */,
13BCE8091C99CB9D00DD7AAD /* RCTRootShadowView.m in Sources */,
14C2CA711B3AC63800E6CBB2 /* RCTModuleMethod.m in Sources */,
13CC8A821B17642100940AE7 /* RCTBorderDrawing.m in Sources */,
83CBBA511A601E3B00E9B192 /* RCTAssert.m in Sources */,

View File

@ -0,0 +1,26 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTShadowView.h"
@interface RCTRootShadowView : RCTShadowView
/**
* Size flexibility type used to find size constraints.
* Default to RCTRootViewSizeFlexibilityNone
*/
@property (nonatomic, assign) RCTRootViewSizeFlexibility sizeFlexibility;
/**
* Calculate all views whose frame needs updating after layout has been calculated.
* Returns a set contains the shadowviews that need updating.
*/
- (NSSet<RCTShadowView *> *)collectViewsWithUpdatedFrames;
@end

View File

@ -0,0 +1,44 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTRootShadowView.h"
@implementation RCTRootShadowView
- (void)applySizeConstraints
{
switch (_sizeFlexibility) {
case RCTRootViewSizeFlexibilityNone:
break;
case RCTRootViewSizeFlexibilityWidth:
self.cssNode->style.dimensions[CSS_WIDTH] = CSS_UNDEFINED;
break;
case RCTRootViewSizeFlexibilityHeight:
self.cssNode->style.dimensions[CSS_HEIGHT] = CSS_UNDEFINED;
break;
case RCTRootViewSizeFlexibilityWidthAndHeight:
self.cssNode->style.dimensions[CSS_WIDTH] = CSS_UNDEFINED;
self.cssNode->style.dimensions[CSS_HEIGHT] = CSS_UNDEFINED;
break;
}
}
- (NSSet<RCTShadowView *> *)collectViewsWithUpdatedFrames
{
[self applySizeConstraints];
[self fillCSSNode:self.cssNode];
layoutNode(self.cssNode, CSS_UNDEFINED, CSS_UNDEFINED, CSS_DIRECTION_INHERIT);
NSMutableSet<RCTShadowView *> *viewsWithNewFrame = [NSMutableSet set];
[self applyLayoutNode:self.cssNode viewsWithNewFrame:viewsWithNewFrame absolutePosition:CGPointZero];
return viewsWithNewFrame;
}
@end

View File

@ -74,12 +74,6 @@ typedef void (^RCTApplierBlock)(NSDictionary<NSNumber *, UIView *> *viewRegistry
*/
- (void)setIntrinsicContentSize:(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 }.
*/
@ -139,12 +133,6 @@ typedef void (^RCTApplierBlock)(NSDictionary<NSNumber *, UIView *> *viewRegistry
- (NSDictionary<NSString *, id> *)processUpdatedProperties:(NSMutableSet<RCTApplierBlock> *)applierBlocks
parentProperties:(NSDictionary<NSString *, id> *)parentProperties NS_REQUIRES_SUPER;
/**
* Calculate all views whose frame needs updating after layout has been calculated.
* Returns a set contains the shadowviews that need updating.
*/
- (NSSet<RCTShadowView *> *)collectRootUpdatedFrames;
/**
* Recursively apply layout to children.
*/

View File

@ -214,40 +214,6 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st
}
}
- (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;
}
}
- (NSSet<RCTShadowView *> *)collectRootUpdatedFrames
{
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_UNDEFINED, CSS_DIRECTION_INHERIT);
NSMutableSet<RCTShadowView *> *viewsWithNewFrame = [NSMutableSet set];
[self applyLayoutNode:_cssNode viewsWithNewFrame:viewsWithNewFrame absolutePosition:CGPointZero];
return viewsWithNewFrame;
}
- (CGRect)measureLayoutRelativeToAncestor:(RCTShadowView *)ancestor
{
CGPoint offset = CGPointZero;
@ -270,7 +236,6 @@ 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;
@ -535,8 +500,7 @@ RCT_POSITION_PROPERTY(Left, left, LEFT)
[self dirtyLayout];
}
static inline BOOL
RCTAssignSuggestedDimension(css_node_t *css_node, int dimension, CGFloat amount)
static inline BOOL RCTAssignSuggestedDimension(css_node_t *css_node, int dimension, CGFloat amount)
{
if (amount != UIViewNoIntrinsicMetric
&& isnan(css_node->style.dimensions[dimension])) {