diff --git a/RNTester/RNTesterUnitTests/RCTComponentPropsTests.m b/RNTester/RNTesterUnitTests/RCTComponentPropsTests.m index fc994c77072..9b66322d70d 100644 --- a/RNTester/RNTesterUnitTests/RCTComponentPropsTests.m +++ b/RNTester/RNTesterUnitTests/RCTComponentPropsTests.m @@ -124,6 +124,33 @@ RCT_CUSTOM_VIEW_PROPERTY(customProp, NSString, RCTPropsTestView) RCT_RUN_RUNLOOP_WHILE(view == nil); } +- (void)testNeedsOffscreenAlphaCompositing +{ + __block RCTPropsTestView *view; + RCTUIManager *uiManager = _bridge.uiManager; + + XCTestExpectation *initialExpectation = [self expectationWithDescription:@"initial expectation"]; + XCTestExpectation *updateExpectation = [self expectationWithDescription:@"second expectation"]; + + dispatch_async(uiManager.methodQueue, ^{ + [uiManager createView:@2 viewName:@"RCTPropsTestView" rootTag:self->_rootViewReactTag props:@{}]; + [uiManager addUIBlock:^(__unused RCTUIManager *_uiManager, NSDictionary *viewRegistry) { + view = (RCTPropsTestView *)viewRegistry[@2]; + XCTAssertEqual(view.layer.allowsGroupOpacity, TRUE); + [initialExpectation fulfill]; + }]; + [uiManager updateView:@2 viewName:@"RCTPropsTestView" props:@{@"needsOffscreenAlphaCompositing": @NO}]; + [uiManager addUIBlock:^(__unused RCTUIManager *_uiManager, NSDictionary *viewRegistry) { + view = (RCTPropsTestView *)viewRegistry[@2]; + XCTAssertEqual(view.layer.allowsGroupOpacity, FALSE); + [updateExpectation fulfill]; + }]; + [uiManager setNeedsLayout]; + }); + + [self waitForExpectations:@[initialExpectation, updateExpectation] timeout:0.1]; +} + - (void)testResetProps { __block RCTPropsTestView *view; diff --git a/RNTester/js/ViewExample.js b/RNTester/js/ViewExample.js index 58d53700858..832be7f51be 100644 --- a/RNTester/js/ViewExample.js +++ b/RNTester/js/ViewExample.js @@ -225,6 +225,93 @@ exports.examples = [ ); }, }, + { + title: 'Offscreen Alpha Compositing', + render() { + type Props = $ReadOnly<{||}>; + type State = {| + active: boolean, + |}; + + const styles = StyleSheet.create({ + alphaCompositing: { + justifyContent: 'space-around', + width: 100, + height: 50, + borderRadius: 100, + }, + }); + + class OffscreenAlphaCompositing extends React.Component { + state = { + active: false, + }; + + render() { + return ( + + + Blobs + + + + + + Same blobs, but their shared container have 0.5 opacity + + + Tap to {this.state.active ? 'activate' : 'deactivate'}{' '} + needsOffscreenAlphaCompositing + + + + + + + + ); + } + + _handlePress = () => { + this.setState({active: !this.state.active}); + }; + } + + return ; + }, + }, { title: 'ZIndex', render() { diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m index 0c8a4627e17..c6570b58bcd 100644 --- a/React/Views/RCTViewManager.m +++ b/React/Views/RCTViewManager.m @@ -129,6 +129,7 @@ RCT_REMAP_VIEW_PROPERTY(shadowColor, layer.shadowColor, CGColor) RCT_REMAP_VIEW_PROPERTY(shadowOffset, layer.shadowOffset, CGSize) RCT_REMAP_VIEW_PROPERTY(shadowOpacity, layer.shadowOpacity, float) RCT_REMAP_VIEW_PROPERTY(shadowRadius, layer.shadowRadius, CGFloat) +RCT_REMAP_VIEW_PROPERTY(needsOffscreenAlphaCompositing, layer.allowsGroupOpacity, BOOL) RCT_CUSTOM_VIEW_PROPERTY(overflow, YGOverflow, RCTView) { if (json) {