Fix InputAccessoryView safe area when not attached to a TextInput (#21179)

Summary:
When using an InputAccessoryView attached to a TextInput the safe area insets are not applied properly. This uses different autolayout constraints that works in all cases I tested, roughly based on the technique used here https://github.com/stockx/SafeAreaInputAccessoryViewWrapperView/blob/master/SafeAreaInputAccessoryViewWrapperView/Classes/SafeAreaInputAccessoryViewWrapperView.swift#L38.
Pull Request resolved: https://github.com/facebook/react-native/pull/21179

Differential Revision: D9928503

Pulled By: hramos

fbshipit-source-id: b1b623334558093042fd94ac85e1b52dd16aa1a0
This commit is contained in:
Janic Duplessis 2018-09-18 18:23:55 -07:00 committed by Facebook Github Bot
parent a0f7d6090f
commit 2191eecf54
3 changed files with 47 additions and 20 deletions

View File

@ -42,7 +42,7 @@
- (void)reactSetFrame:(CGRect)frame
{
[_inputAccessoryView setFrame:frame];
[_inputAccessoryView reactSetFrame:frame];
if (_shouldBecomeFirstResponder) {
_shouldBecomeFirstResponder = NO;

View File

@ -12,6 +12,7 @@
@implementation RCTInputAccessoryViewContent
{
UIView *_safeAreaContainer;
NSLayoutConstraint *_heightConstraint;
}
- (instancetype)init
@ -19,31 +20,48 @@
if (self = [super init]) {
_safeAreaContainer = [UIView new];
[self addSubview:_safeAreaContainer];
// Use autolayout to position the view properly and take into account
// safe area insets on iPhone X.
// TODO: Support rotation, anchor to left and right without breaking frame x coordinate (T27974328).
self.autoresizingMask = UIViewAutoresizingFlexibleHeight;
_safeAreaContainer.translatesAutoresizingMaskIntoConstraints = NO;
_heightConstraint = [_safeAreaContainer.heightAnchor constraintEqualToConstant:0];
_heightConstraint.active = YES;
if (@available(iOS 11.0, *)) {
[_safeAreaContainer.bottomAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.bottomAnchor].active = YES;
[_safeAreaContainer.topAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.topAnchor].active = YES;
[_safeAreaContainer.leadingAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.leadingAnchor].active = YES;
[_safeAreaContainer.trailingAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.trailingAnchor].active = YES;
} else {
[_safeAreaContainer.bottomAnchor constraintEqualToAnchor:self.bottomAnchor].active = YES;
[_safeAreaContainer.topAnchor constraintEqualToAnchor:self.topAnchor].active = YES;
[_safeAreaContainer.leadingAnchor constraintEqualToAnchor:self.leadingAnchor].active = YES;
[_safeAreaContainer.trailingAnchor constraintEqualToAnchor:self.trailingAnchor].active = YES;
}
}
return self;
}
- (void)didMoveToSuperview
- (CGSize)intrinsicContentSize
{
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
// Avoid the home pill (in portrait mode)
// TODO: Support rotation, anchor to left and right without breaking frame x coordinate (T27974328).
if (@available(iOS 11.0, *)) {
if (self.window) {
[_safeAreaContainer.bottomAnchor
constraintLessThanOrEqualToSystemSpacingBelowAnchor:self.window.safeAreaLayoutGuide.bottomAnchor
multiplier:1.0f].active = YES;
}
}
#endif
// This is needed so the view size is based on autolayout constraints.
return CGSizeZero;
}
- (void)setFrame:(CGRect)frame
- (void)reactSetFrame:(CGRect)frame
{
[super setFrame:frame];
// We still need to set the frame here, otherwise it won't be
// measured until moved to the window during the keyboard opening
// animation. If this happens, the height will be animated from 0 to
// its actual size and we don't want that.
[self setFrame:frame];
[_safeAreaContainer setFrame:frame];
_heightConstraint.constant = frame.size.height;
[self layoutIfNeeded];
}
- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)index

View File

@ -58,6 +58,8 @@ class TextInputBar extends React.PureComponent<*, *> {
}
}
const BAR_HEIGHT = 44;
class InputAccessoryViewExample extends React.Component<*> {
static title = '<InputAccessoryView>';
static description =
@ -65,8 +67,8 @@ class InputAccessoryViewExample extends React.Component<*> {
render() {
return (
<View>
<ScrollView keyboardDismissMode="interactive">
<>
<ScrollView style={styles.fill} keyboardDismissMode="interactive">
{Array(15)
.fill()
.map((_, i) => <Message key={i} />)}
@ -74,14 +76,21 @@ class InputAccessoryViewExample extends React.Component<*> {
<InputAccessoryView backgroundColor="#fffffff7">
<TextInputBar />
</InputAccessoryView>
</View>
</>
);
}
}
const styles = StyleSheet.create({
fill: {
flex: 1,
},
textInputContainer: {
flexDirection: 'row',
alignItems: 'center',
borderTopWidth: 1,
borderTopColor: '#eee',
height: BAR_HEIGHT,
},
textInput: {
flex: 1,