SafeAreaView: A new prop `emulateUnlessSupported` that turns off the custom implementation of `safeAreaInsets` insets

Summary: In some cases, the custom implementation of this prop is undesirable, so this allows to turn it off.

Reviewed By: yungsters

Differential Revision: D9759228

fbshipit-source-id: 4f61cd900c2da9046977c11a61606a4f5f961177
This commit is contained in:
Valentin Shergin 2018-09-11 21:14:44 -07:00 committed by Facebook Github Bot
parent d6b9ec1c1f
commit 454aa02210
7 changed files with 130 additions and 78 deletions

View File

@ -1,13 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow
*/
'use strict';
module.exports = require('View');

View File

@ -1,40 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
const DeprecatedViewPropTypes = require('DeprecatedViewPropTypes');
const React = require('React');
const requireNativeComponent = require('requireNativeComponent');
import type {ViewProps} from 'ViewPropTypes';
const RCTSafeAreaView = requireNativeComponent('RCTSafeAreaView');
type Props = ViewProps & {
children: any,
};
/**
* Renders nested content and automatically applies paddings reflect the portion of the view
* that is not covered by navigation bars, tab bars, toolbars, and other ancestor views.
* Moreover, and most importantly, Safe Area's paddings reflect physical limitation of the screen,
* such as rounded corners or camera notches (aka sensor housing area on iPhone X).
*/
class SafeAreaView extends React.Component<Props> {
static propTypes = {
...DeprecatedViewPropTypes,
};
render() {
return <RCTSafeAreaView {...this.props} />;
}
}
module.exports = SafeAreaView;

View File

@ -0,0 +1,50 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
const Platform = require('Platform');
const React = require('React');
const View = require('View');
const requireNativeComponent = require('requireNativeComponent');
import type {ViewProps} from 'ViewPropTypes';
type Props = $ReadOnly<{|
...ViewProps,
emulateUnlessSupported?: boolean,
|}>;
let exported;
/**
* Renders nested content and automatically applies paddings reflect the portion
* of the view that is not covered by navigation bars, tab bars, toolbars, and
* other ancestor views.
*
* Moreover, and most importantly, Safe Area's paddings reflect physical
* limitation of the screen, such as rounded corners or camera notches (aka
* sensor housing area on iPhone X).
*/
if (Platform.OS === 'android') {
exported = class SafeAreaView extends React.Component<Props> {
render(): React.Node {
const {emulateUnlessSupported, ...props} = this.props;
return <View {...props} />;
}
};
} else {
const RCTSafeAreaView = requireNativeComponent('RCTSafeAreaView');
exported = class SafeAreaView extends React.Component<Props> {
render(): React.Node {
return <RCTSafeAreaView emulateUnlessSupported={true} {...this.props} />;
}
};
}
module.exports = exported;

View File

@ -15,6 +15,7 @@ const Modal = require('Modal');
const React = require('react');
const SafeAreaView = require('SafeAreaView');
const StyleSheet = require('StyleSheet');
const Switch = require('Switch');
const Text = require('Text');
const View = require('View');
@ -26,10 +27,14 @@ exports.description =
class SafeAreaViewExample extends React.Component<
{},
{|modalVisible: boolean|},
{|
modalVisible: boolean,
emulateUnlessSupported: boolean,
|},
> {
state = {
modalVisible: false,
emulateUnlessSupported: true,
};
_setModalVisible = visible => {
@ -45,12 +50,21 @@ class SafeAreaViewExample extends React.Component<
animationType="slide"
supportedOrientations={['portrait', 'landscape']}>
<View style={styles.modal}>
<SafeAreaView style={styles.safeArea}>
<SafeAreaView
style={styles.safeArea}
emulateUnlessSupported={this.state.emulateUnlessSupported}>
<View style={styles.safeAreaContent}>
<Button
onPress={this._setModalVisible.bind(this, false)}
title="Close"
/>
<Text>emulateUnlessSupported:</Text>
<Switch
onValueChange={value =>
this.setState({emulateUnlessSupported: value})
}
value={this.state.emulateUnlessSupported}
/>
</View>
</SafeAreaView>
</View>
@ -59,6 +73,13 @@ class SafeAreaViewExample extends React.Component<
onPress={this._setModalVisible.bind(this, true)}
title="Present Modal Screen with SafeAreaView"
/>
<Text>emulateUnlessSupported:</Text>
<Switch
onValueChange={value =>
this.setState({emulateUnlessSupported: value})
}
value={this.state.emulateUnlessSupported}
/>
</View>
);
}

View File

@ -17,6 +17,8 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initWithBridge:(RCTBridge *)bridge;
@property (nonatomic, assign) BOOL emulateUnlessSupported;
@end
NS_ASSUME_NONNULL_END

View File

@ -21,6 +21,7 @@
{
if (self = [super initWithFrame:CGRectZero]) {
_bridge = bridge;
_emulateUnlessSupported = YES; // The default.
}
return self;
@ -29,38 +30,29 @@
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)decoder)
RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
static BOOL UIEdgeInsetsEqualToEdgeInsetsWithThreshold(UIEdgeInsets insets1, UIEdgeInsets insets2, CGFloat threshold) {
return
ABS(insets1.left - insets2.left) <= threshold &&
ABS(insets1.right - insets2.right) <= threshold &&
ABS(insets1.top - insets2.top) <= threshold &&
ABS(insets1.bottom - insets2.bottom) <= threshold;
- (BOOL)isSupportedByOS
{
return [self respondsToSelector:@selector(safeAreaInsets)];
}
- (UIEdgeInsets)safeAreaInsetsIfSupportedAndEnabled
{
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
- (void)safeAreaInsetsDidChange
{
if (![self respondsToSelector:@selector(safeAreaInsets)]) {
return;
if (self.isSupportedByOS) {
return self.safeAreaInsets;
}
[self setSafeAreaInsets:self.safeAreaInsets];
#endif
return self.emulateUnlessSupported ? self.emulatedSafeAreaInsets : UIEdgeInsetsZero;
}
#endif
// Emulate safe area for iOS < 11
- (void)layoutSubviews
- (UIEdgeInsets)emulatedSafeAreaInsets
{
[super layoutSubviews];
if ([self respondsToSelector:@selector(safeAreaInsets)]) {
return;
}
UIViewController* vc = self.reactViewController;
if (!vc) {
return;
return UIEdgeInsetsZero;
}
CGFloat topLayoutOffset = vc.topLayoutGuide.length;
CGFloat bottomLayoutOffset = vc.bottomLayoutGuide.length;
CGRect safeArea = vc.view.bounds;
@ -75,7 +67,34 @@ static BOOL UIEdgeInsetsEqualToEdgeInsetsWithThreshold(UIEdgeInsets insets1, UIE
safeAreaInsets.bottom = CGRectGetMaxY(self.bounds) - CGRectGetMaxY(localSafeArea);
}
[self setSafeAreaInsets:safeAreaInsets];
return safeAreaInsets;
}
static BOOL UIEdgeInsetsEqualToEdgeInsetsWithThreshold(UIEdgeInsets insets1, UIEdgeInsets insets2, CGFloat threshold) {
return
ABS(insets1.left - insets2.left) <= threshold &&
ABS(insets1.right - insets2.right) <= threshold &&
ABS(insets1.top - insets2.top) <= threshold &&
ABS(insets1.bottom - insets2.bottom) <= threshold;
}
- (void)safeAreaInsetsDidChange
{
[self invalidateSafeAreaInsets];
}
- (void)invalidateSafeAreaInsets
{
[self setSafeAreaInsets:self.safeAreaInsetsIfSupportedAndEnabled];
}
- (void)layoutSubviews
{
[super layoutSubviews];
if (!self.isSupportedByOS && self.emulateUnlessSupported) {
[self invalidateSafeAreaInsets];
}
}
- (void)setSafeAreaInsets:(UIEdgeInsets)safeAreaInsets
@ -90,4 +109,15 @@ static BOOL UIEdgeInsetsEqualToEdgeInsetsWithThreshold(UIEdgeInsets insets1, UIE
[_bridge.uiManager setLocalData:localData forView:self];
}
- (void)setEmulateUnlessSupported:(BOOL)emulateUnlessSupported
{
if (_emulateUnlessSupported == emulateUnlessSupported) {
return;
}
_emulateUnlessSupported = emulateUnlessSupported;
[self invalidateSafeAreaInsets];
}
@end

View File

@ -15,6 +15,8 @@
RCT_EXPORT_MODULE()
RCT_EXPORT_VIEW_PROPERTY(emulateUnlessSupported, BOOL)
- (UIView *)view
{
return [[RCTSafeAreaView alloc] initWithBridge:self.bridge];