diff --git a/Libraries/Text/RCTTextManager.m b/Libraries/Text/RCTTextManager.m index b2832a800..31ae67c03 100644 --- a/Libraries/Text/RCTTextManager.m +++ b/Libraries/Text/RCTTextManager.m @@ -51,11 +51,7 @@ RCT_EXPORT_SHADOW_PROPERTY(maximumNumberOfLines, NSInteger) RCT_EXPORT_SHADOW_PROPERTY(shadowOffset, CGSize) RCT_EXPORT_SHADOW_PROPERTY(textAlign, NSTextAlignment) RCT_REMAP_SHADOW_PROPERTY(backgroundColor, textBackgroundColor, UIColor) -RCT_CUSTOM_SHADOW_PROPERTY(containerBackgroundColor, UIColor, RCTShadowText) -{ - view.backgroundColor = json ? [RCTConvert UIColor:json] : defaultView.backgroundColor; - view.isBGColorExplicitlySet = json ? YES : defaultView.isBGColorExplicitlySet; -} +RCT_REMAP_SHADOW_PROPERTY(containerBackgroundColor, backgroundColor, UIColor) RCT_CUSTOM_SHADOW_PROPERTY(numberOfLines, NSInteger, RCTShadowText) { NSLineBreakMode truncationMode = NSLineBreakByClipping; diff --git a/React/Base/RCTRootView.m b/React/Base/RCTRootView.m index 980d253d3..23100f5e2 100644 --- a/React/Base/RCTRootView.m +++ b/React/Base/RCTRootView.m @@ -48,10 +48,11 @@ RCTBridge *_bridge; NSString *_moduleName; NSDictionary *_launchOptions; + UIColor *_backgroundColor; RCTRootContentView *_contentView; } - - (instancetype)initWithBridge:(RCTBridge *)bridge +- (instancetype)initWithBridge:(RCTBridge *)bridge moduleName:(NSString *)moduleName { RCTAssertMainThread(); @@ -87,6 +88,17 @@ return [self initWithBridge:bridge moduleName:moduleName]; } +- (void)setBackgroundColor:(UIColor *)backgroundColor +{ + _backgroundColor = backgroundColor; + _contentView.backgroundColor = backgroundColor; +} + +- (UIColor *)backgroundColor +{ + return _backgroundColor; +} + - (UIViewController *)backingViewController { return _backingViewController ?: [super backingViewController]; @@ -100,7 +112,6 @@ RCT_IMPORT_METHOD(AppRegistry, runApplication) RCT_IMPORT_METHOD(ReactNative, unmountComponentAtNodeAndRemoveContainer) - - (void)javaScriptDidLoad:(NSNotification *)notification { RCTBridge *bridge = notification.userInfo[@"bridge"]; @@ -124,6 +135,7 @@ RCT_IMPORT_METHOD(ReactNative, unmountComponentAtNodeAndRemoveContainer) [_contentView removeFromSuperview]; _contentView = [[RCTRootContentView alloc] initWithFrame:self.bounds bridge:bridge]; + _contentView.backgroundColor = _backgroundColor; [self addSubview:_contentView]; NSString *moduleName = _moduleName ?: @""; @@ -187,9 +199,17 @@ RCT_IMPORT_METHOD(ReactNative, unmountComponentAtNodeAndRemoveContainer) - (void)setFrame:(CGRect)frame { - [super setFrame:frame]; + super.frame = frame; if (self.reactTag && _bridge.isValid) { - [_bridge.uiManager setFrame:self.bounds forRootView:self]; + [_bridge.uiManager setFrame:frame forRootView:self]; + } +} + +- (void)setBackgroundColor:(UIColor *)backgroundColor +{ + super.backgroundColor = backgroundColor; + if (self.reactTag && _bridge.isValid) { + [_bridge.uiManager setBackgroundColor:backgroundColor forRootView:self]; } } diff --git a/React/Modules/RCTUIManager.h b/React/Modules/RCTUIManager.h index 6fa0e3249..829c1aeff 100644 --- a/React/Modules/RCTUIManager.h +++ b/React/Modules/RCTUIManager.h @@ -36,10 +36,16 @@ /** * Update the frame of a root view. This might be in response to a screen rotation - * or some other layout event outsde of the React-managed view hierarchy. + * or some other layout event outside of the React-managed view hierarchy. */ - (void)setFrame:(CGRect)frame forRootView:(UIView *)rootView; +/** + * 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. + */ +- (void)setBackgroundColor:(UIColor *)color forRootView:(UIView *)rootView; + /** * Schedule a block to be executed on the UI thread. Useful if you need to execute * view logic after all currently queued view updates have completed. diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index ec12aa6fb..6b88f05d3 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -338,17 +338,14 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass, NSString *viewNa _viewRegistry[reactTag] = rootView; CGRect frame = rootView.frame; - // Register manager (TODO: should we do this, or leave it nil?) - _viewManagerRegistry[reactTag] = _viewManagers[@"RCTView"]; - // Register shadow view dispatch_async(_shadowQueue, ^{ RCTShadowView *shadowView = [[RCTShadowView alloc] init]; shadowView.reactTag = reactTag; shadowView.frame = frame; - shadowView.backgroundColor = [UIColor whiteColor]; + shadowView.backgroundColor = rootView.backgroundColor; + shadowView.viewName = NSStringFromClass([rootView class]); _shadowViewRegistry[shadowView.reactTag] = shadowView; - [_rootViewTags addObject:reactTag]; }); } @@ -372,6 +369,22 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass, NSString *viewNa }); } +- (void)setBackgroundColor:(UIColor *)color forRootView:(UIView *)rootView +{ + RCTAssertMainThread(); + + NSNumber *reactTag = rootView.reactTag; + RCTAssert(RCTIsReactRootView(reactTag), @"Specified view %@ is not a root view", reactTag); + + dispatch_async(_shadowQueue, ^{ + RCTShadowView *rootShadowView = _shadowViewRegistry[reactTag]; + RCTAssert(rootShadowView != nil, @"Could not locate root view with tag #%@", reactTag); + rootShadowView.backgroundColor = color; + [self _amendPendingUIBlocksWithStylePropagationUpdateForRootView:rootShadowView]; + [self flushUIBlocks]; + }); +} + /** * Unregisters views from registries */ @@ -799,6 +812,11 @@ RCT_EXPORT_METHOD(createView:(NSNumber *)reactTag } _shadowViewRegistry[reactTag] = shadowView; + // Shadow view is the source of truth for background color this is a little + // bit counter-intuitive if people try to set background color when setting up + // the view, but it's the only way that makes sense given our threading model + UIColor *backgroundColor = shadowView.backgroundColor; + [self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){ RCTAssertMainThread(); @@ -807,14 +825,15 @@ RCT_EXPORT_METHOD(createView:(NSNumber *)reactTag // Generate default view, used for resetting default props if (!uiManager->_defaultViews[viewName]) { - // Note the default is setup after the props are read for the first time ever - // for this className - this is ok because we only use the default for restoring - // defaults, which never happens on first creation. + // Note the default is setup after the props are read for the first time + // ever for this className - this is ok because we only use the default + // for restoring defaults, which never happens on first creation. uiManager->_defaultViews[viewName] = [manager view]; } // Set properties view.reactTag = reactTag; + view.backgroundColor = backgroundColor; if ([view isKindOfClass:[UIView class]]) { view.multipleTouchEnabled = YES; view.userInteractionEnabled = YES; // required for touch handling diff --git a/React/Views/RCTShadowView.h b/React/Views/RCTShadowView.h index 83350ac46..c2e10750d 100644 --- a/React/Views/RCTShadowView.h +++ b/React/Views/RCTShadowView.h @@ -38,7 +38,6 @@ typedef void (^RCTApplierBlock)(RCTSparseArray *); @property (nonatomic, weak, readonly) RCTShadowView *superview; @property (nonatomic, assign, readonly) css_node_t *cssNode; @property (nonatomic, copy) NSString *viewName; -@property (nonatomic, assign) BOOL isBGColorExplicitlySet; // Used to propagate to children @property (nonatomic, strong) UIColor *backgroundColor; // Used to propagate to children @property (nonatomic, assign) RCTUpdateLifecycle layoutLifecycle; @property (nonatomic, assign) BOOL hasOnLayout; diff --git a/React/Views/RCTShadowView.m b/React/Views/RCTShadowView.m index e5975dff2..7a0e9e421 100644 --- a/React/Views/RCTShadowView.m +++ b/React/Views/RCTShadowView.m @@ -38,6 +38,7 @@ typedef enum { NSMutableArray *_reactSubviews; BOOL _recomputePadding; BOOL _recomputeMargin; + BOOL _isBGColorExplicitlySet; float _paddingMetaProps[META_PROP_COUNT]; float _marginMetaProps[META_PROP_COUNT]; } @@ -515,6 +516,7 @@ RCT_STYLE_PROPERTY(FlexWrap, flexWrap, flex_wrap, css_wrap_type_t) - (void)setBackgroundColor:(UIColor *)color { _backgroundColor = color; + _isBGColorExplicitlySet = YES; [self dirtyPropagation]; } diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m index 8230e398d..311167761 100644 --- a/React/Views/RCTViewManager.m +++ b/React/Views/RCTViewManager.m @@ -228,36 +228,38 @@ RCT_VIEW_BORDER_RADIUS_PROPERTY(BottomRight) #pragma mark - ShadowView properties -RCT_EXPORT_SHADOW_PROPERTY(top, CGFloat); -RCT_EXPORT_SHADOW_PROPERTY(right, CGFloat); -RCT_EXPORT_SHADOW_PROPERTY(bottom, CGFloat); +RCT_EXPORT_SHADOW_PROPERTY(backgroundColor, UIColor) + +RCT_EXPORT_SHADOW_PROPERTY(top, CGFloat) +RCT_EXPORT_SHADOW_PROPERTY(right, CGFloat) +RCT_EXPORT_SHADOW_PROPERTY(bottom, CGFloat) RCT_EXPORT_SHADOW_PROPERTY(left, CGFloat); -RCT_EXPORT_SHADOW_PROPERTY(width, CGFloat); -RCT_EXPORT_SHADOW_PROPERTY(height, CGFloat); +RCT_EXPORT_SHADOW_PROPERTY(width, CGFloat) +RCT_EXPORT_SHADOW_PROPERTY(height, CGFloat) -RCT_EXPORT_SHADOW_PROPERTY(borderTopWidth, CGFloat); -RCT_EXPORT_SHADOW_PROPERTY(borderRightWidth, CGFloat); -RCT_EXPORT_SHADOW_PROPERTY(borderBottomWidth, CGFloat); -RCT_EXPORT_SHADOW_PROPERTY(borderLeftWidth, CGFloat); +RCT_EXPORT_SHADOW_PROPERTY(borderTopWidth, CGFloat) +RCT_EXPORT_SHADOW_PROPERTY(borderRightWidth, CGFloat) +RCT_EXPORT_SHADOW_PROPERTY(borderBottomWidth, CGFloat) +RCT_EXPORT_SHADOW_PROPERTY(borderLeftWidth, CGFloat) RCT_CUSTOM_SHADOW_PROPERTY(borderWidth, CGFloat, RCTShadowView) { [view setBorderWidth:[RCTConvert CGFloat:json]]; } -RCT_EXPORT_SHADOW_PROPERTY(marginTop, CGFloat); -RCT_EXPORT_SHADOW_PROPERTY(marginRight, CGFloat); -RCT_EXPORT_SHADOW_PROPERTY(marginBottom, CGFloat); -RCT_EXPORT_SHADOW_PROPERTY(marginLeft, CGFloat); -RCT_EXPORT_SHADOW_PROPERTY(marginVertical, CGFloat); -RCT_EXPORT_SHADOW_PROPERTY(marginHorizontal, CGFloat); -RCT_EXPORT_SHADOW_PROPERTY(margin, CGFloat); +RCT_EXPORT_SHADOW_PROPERTY(marginTop, CGFloat) +RCT_EXPORT_SHADOW_PROPERTY(marginRight, CGFloat) +RCT_EXPORT_SHADOW_PROPERTY(marginBottom, CGFloat) +RCT_EXPORT_SHADOW_PROPERTY(marginLeft, CGFloat) +RCT_EXPORT_SHADOW_PROPERTY(marginVertical, CGFloat) +RCT_EXPORT_SHADOW_PROPERTY(marginHorizontal, CGFloat) +RCT_EXPORT_SHADOW_PROPERTY(margin, CGFloat) -RCT_EXPORT_SHADOW_PROPERTY(paddingTop, CGFloat); -RCT_EXPORT_SHADOW_PROPERTY(paddingRight, CGFloat); -RCT_EXPORT_SHADOW_PROPERTY(paddingBottom, CGFloat); -RCT_EXPORT_SHADOW_PROPERTY(paddingLeft, CGFloat); -RCT_EXPORT_SHADOW_PROPERTY(paddingVertical, CGFloat); -RCT_EXPORT_SHADOW_PROPERTY(paddingHorizontal, CGFloat); +RCT_EXPORT_SHADOW_PROPERTY(paddingTop, CGFloat) +RCT_EXPORT_SHADOW_PROPERTY(paddingRight, CGFloat) +RCT_EXPORT_SHADOW_PROPERTY(paddingBottom, CGFloat) +RCT_EXPORT_SHADOW_PROPERTY(paddingLeft, CGFloat) +RCT_EXPORT_SHADOW_PROPERTY(paddingVertical, CGFloat) +RCT_EXPORT_SHADOW_PROPERTY(paddingHorizontal, CGFloat) RCT_EXPORT_SHADOW_PROPERTY(padding, CGFloat); RCT_EXPORT_SHADOW_PROPERTY(flex, CGFloat) @@ -268,12 +270,6 @@ RCT_EXPORT_SHADOW_PROPERTY(alignItems, css_align_t) RCT_EXPORT_SHADOW_PROPERTY(alignSelf, css_align_t) RCT_REMAP_SHADOW_PROPERTY(position, positionType, css_position_type_t) -RCT_CUSTOM_SHADOW_PROPERTY(backgroundColor, UIColor, RCTShadowView) -{ - view.backgroundColor = json ? [RCTConvert UIColor:json] : defaultView.backgroundColor; - view.isBGColorExplicitlySet = json ? YES : defaultView.isBGColorExplicitlySet; -} - RCT_REMAP_SHADOW_PROPERTY(onLayout, hasOnLayout, BOOL) @end