mirror of
https://github.com/status-im/react-native.git
synced 2025-02-04 13:44:04 +00:00
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:
parent
d6b9ec1c1f
commit
454aa02210
@ -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');
|
|
@ -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;
|
|
50
Libraries/Components/SafeAreaView/SafeAreaView.js
Normal file
50
Libraries/Components/SafeAreaView/SafeAreaView.js
Normal 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;
|
@ -15,6 +15,7 @@ const Modal = require('Modal');
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const SafeAreaView = require('SafeAreaView');
|
const SafeAreaView = require('SafeAreaView');
|
||||||
const StyleSheet = require('StyleSheet');
|
const StyleSheet = require('StyleSheet');
|
||||||
|
const Switch = require('Switch');
|
||||||
const Text = require('Text');
|
const Text = require('Text');
|
||||||
const View = require('View');
|
const View = require('View');
|
||||||
|
|
||||||
@ -26,10 +27,14 @@ exports.description =
|
|||||||
|
|
||||||
class SafeAreaViewExample extends React.Component<
|
class SafeAreaViewExample extends React.Component<
|
||||||
{},
|
{},
|
||||||
{|modalVisible: boolean|},
|
{|
|
||||||
|
modalVisible: boolean,
|
||||||
|
emulateUnlessSupported: boolean,
|
||||||
|
|},
|
||||||
> {
|
> {
|
||||||
state = {
|
state = {
|
||||||
modalVisible: false,
|
modalVisible: false,
|
||||||
|
emulateUnlessSupported: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
_setModalVisible = visible => {
|
_setModalVisible = visible => {
|
||||||
@ -45,12 +50,21 @@ class SafeAreaViewExample extends React.Component<
|
|||||||
animationType="slide"
|
animationType="slide"
|
||||||
supportedOrientations={['portrait', 'landscape']}>
|
supportedOrientations={['portrait', 'landscape']}>
|
||||||
<View style={styles.modal}>
|
<View style={styles.modal}>
|
||||||
<SafeAreaView style={styles.safeArea}>
|
<SafeAreaView
|
||||||
|
style={styles.safeArea}
|
||||||
|
emulateUnlessSupported={this.state.emulateUnlessSupported}>
|
||||||
<View style={styles.safeAreaContent}>
|
<View style={styles.safeAreaContent}>
|
||||||
<Button
|
<Button
|
||||||
onPress={this._setModalVisible.bind(this, false)}
|
onPress={this._setModalVisible.bind(this, false)}
|
||||||
title="Close"
|
title="Close"
|
||||||
/>
|
/>
|
||||||
|
<Text>emulateUnlessSupported:</Text>
|
||||||
|
<Switch
|
||||||
|
onValueChange={value =>
|
||||||
|
this.setState({emulateUnlessSupported: value})
|
||||||
|
}
|
||||||
|
value={this.state.emulateUnlessSupported}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
</View>
|
</View>
|
||||||
@ -59,6 +73,13 @@ class SafeAreaViewExample extends React.Component<
|
|||||||
onPress={this._setModalVisible.bind(this, true)}
|
onPress={this._setModalVisible.bind(this, true)}
|
||||||
title="Present Modal Screen with SafeAreaView"
|
title="Present Modal Screen with SafeAreaView"
|
||||||
/>
|
/>
|
||||||
|
<Text>emulateUnlessSupported:</Text>
|
||||||
|
<Switch
|
||||||
|
onValueChange={value =>
|
||||||
|
this.setState({emulateUnlessSupported: value})
|
||||||
|
}
|
||||||
|
value={this.state.emulateUnlessSupported}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
|
|
||||||
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
||||||
|
|
||||||
|
@property (nonatomic, assign) BOOL emulateUnlessSupported;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
NS_ASSUME_NONNULL_END
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
{
|
{
|
||||||
if (self = [super initWithFrame:CGRectZero]) {
|
if (self = [super initWithFrame:CGRectZero]) {
|
||||||
_bridge = bridge;
|
_bridge = bridge;
|
||||||
|
_emulateUnlessSupported = YES; // The default.
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@ -29,38 +30,29 @@
|
|||||||
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)decoder)
|
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)decoder)
|
||||||
RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
|
RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
|
||||||
|
|
||||||
static BOOL UIEdgeInsetsEqualToEdgeInsetsWithThreshold(UIEdgeInsets insets1, UIEdgeInsets insets2, CGFloat threshold) {
|
- (BOOL)isSupportedByOS
|
||||||
return
|
{
|
||||||
ABS(insets1.left - insets2.left) <= threshold &&
|
return [self respondsToSelector:@selector(safeAreaInsets)];
|
||||||
ABS(insets1.right - insets2.right) <= threshold &&
|
|
||||||
ABS(insets1.top - insets2.top) <= threshold &&
|
|
||||||
ABS(insets1.bottom - insets2.bottom) <= threshold;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (UIEdgeInsets)safeAreaInsetsIfSupportedAndEnabled
|
||||||
|
{
|
||||||
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
|
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
|
||||||
|
if (self.isSupportedByOS) {
|
||||||
- (void)safeAreaInsetsDidChange
|
return self.safeAreaInsets;
|
||||||
{
|
|
||||||
if (![self respondsToSelector:@selector(safeAreaInsets)]) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
[self setSafeAreaInsets:self.safeAreaInsets];
|
return self.emulateUnlessSupported ? self.emulatedSafeAreaInsets : UIEdgeInsetsZero;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
- (UIEdgeInsets)emulatedSafeAreaInsets
|
||||||
|
|
||||||
// Emulate safe area for iOS < 11
|
|
||||||
- (void)layoutSubviews
|
|
||||||
{
|
{
|
||||||
[super layoutSubviews];
|
|
||||||
if ([self respondsToSelector:@selector(safeAreaInsets)]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
UIViewController* vc = self.reactViewController;
|
UIViewController* vc = self.reactViewController;
|
||||||
|
|
||||||
if (!vc) {
|
if (!vc) {
|
||||||
return;
|
return UIEdgeInsetsZero;
|
||||||
}
|
}
|
||||||
|
|
||||||
CGFloat topLayoutOffset = vc.topLayoutGuide.length;
|
CGFloat topLayoutOffset = vc.topLayoutGuide.length;
|
||||||
CGFloat bottomLayoutOffset = vc.bottomLayoutGuide.length;
|
CGFloat bottomLayoutOffset = vc.bottomLayoutGuide.length;
|
||||||
CGRect safeArea = vc.view.bounds;
|
CGRect safeArea = vc.view.bounds;
|
||||||
@ -75,7 +67,34 @@ static BOOL UIEdgeInsetsEqualToEdgeInsetsWithThreshold(UIEdgeInsets insets1, UIE
|
|||||||
safeAreaInsets.bottom = CGRectGetMaxY(self.bounds) - CGRectGetMaxY(localSafeArea);
|
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
|
- (void)setSafeAreaInsets:(UIEdgeInsets)safeAreaInsets
|
||||||
@ -90,4 +109,15 @@ static BOOL UIEdgeInsetsEqualToEdgeInsetsWithThreshold(UIEdgeInsets insets1, UIE
|
|||||||
[_bridge.uiManager setLocalData:localData forView:self];
|
[_bridge.uiManager setLocalData:localData forView:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setEmulateUnlessSupported:(BOOL)emulateUnlessSupported
|
||||||
|
{
|
||||||
|
if (_emulateUnlessSupported == emulateUnlessSupported) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_emulateUnlessSupported = emulateUnlessSupported;
|
||||||
|
|
||||||
|
[self invalidateSafeAreaInsets];
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
|
|
||||||
RCT_EXPORT_MODULE()
|
RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(emulateUnlessSupported, BOOL)
|
||||||
|
|
||||||
- (UIView *)view
|
- (UIView *)view
|
||||||
{
|
{
|
||||||
return [[RCTSafeAreaView alloc] initWithBridge:self.bridge];
|
return [[RCTSafeAreaView alloc] initWithBridge:self.bridge];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user