From cf5f25472df644b65820115986f9eec8306bd1fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Fossli?= Date: Wed, 13 Feb 2019 16:24:41 -0800 Subject: [PATCH] Add support for needsOffscreenAlphaCompositing on iOS (#19052) Summary: Currently on iOS in UIKit and in RN all views are by default set to `allowsGroupOpacity=true`. Any view that has all of the following - `allowsGroupOpacity` set to true - opacity greater than 0.0 and less than 1.0 - have any subviews will be rendered off screen (on CPU). [See this link for more details](https://stackoverflow.com/questions/13158796/what-triggers-offscreen-rendering-blending-and-layoutsubviews-in-ios/13649143#13649143). Which means performance will be decreased and may affect the user experience. Therefore it makes sense to allow the developers to override this property. This pull request allows for changing `allowsGroupOpacity` via `needsOffscreenAlphaCompositing`. Android already supports this. This is not a new name or variable. See https://facebook.github.io/react-native/docs/view.html#needsoffscreenalphacompositing. Pull Request resolved: https://github.com/facebook/react-native/pull/19052 Differential Revision: D14071300 Pulled By: hramos fbshipit-source-id: 004278801a19463ebf9da6f8855f02ed27926025 --- .../RCTComponentPropsTests.m | 27 ++++++ RNTester/js/ViewExample.js | 87 +++++++++++++++++++ React/Views/RCTViewManager.m | 1 + 3 files changed, 115 insertions(+) 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) {