Brief
When using the component as a wrapper for whole app, sometimes the first iOS key press ever acts like the keyboard is hidden. It runs the animation that hides stuff behind the keyboard.
Closing the keyboard later and opening it again would work just fine until I restart the app.
I did not try in Android.
Below I'll mention what seemed to fix it for me.
Details
I was playing with the sample app from react-native-size-matters. It had a chat window with textbox at the bottom that suffered from the keyboard issue.
After some messing up to upgrade the sample itself to react-native 0.55 (latest expo supports), and running it, and trying KeyboardAvoidingView
then finding facebook/react-native#13393 (comment), I modified the file that wraps all components:
https://github.com/nirsky/react-native-size-matters/blob/16383ea10bafcfd508cfbafb36995f4ea27b7f2a/examples/expo-example-app/components/behaviors/withScaledSheetSwitch.js#L9
Which originally looked like:
https://github.com/nirsky/react-native-size-matters/blob/16383ea10bafcfd508cfbafb36995f4ea27b7f2a/examples/expo-example-app/components/behaviors/withScaledSheetSwitch.js#L9-L17
render() {
return <View style={{flex: 1}}>
<View style={{alignSelf: 'stretch', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', margin: 10}}>
<Text style={{fontSize: 16}}>size-matters enabled?</Text>
<Switch value={this.state.scale} onValueChange={() => this.setState(prevState => ({scale: !prevState.scale}))}/>
</View>
<ChildComponent {...this.props} scale={this.state.scale}/>
</View>
}
To now look like:
return (
<Spacer enabled style={{ height: '100%' }}>
<View style={{flex:1, height: '100%'}}>
<View style={{ alignSelf: 'stretch', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', margin: 10 }}>
<Text style={{ fontSize: 16 }}>size-matters enabled?</Text>
<Switch value={this.state.scale} onValueChange={() => this.setState(prevState => ({ scale: !prevState.scale }))} />
</View>
<ChildComponent {...this.props} scale={this.state.scale} />
</View>
</Spacer>
);
}
(I only added the <Spacer>
and styled it, and gave the <View>
under it height: '100%'
)
To imagine the app (pic from iPad, but I experienced the issue on most recent iPhone simulator, with iOS 12):
Then when I opened the chat page in the app, I click on the textbox at the bottom, and the keyboard popped up with the screen shifting above it nicely, but when I clicked on the keyboard to enter text, the view expanded to full window, hiding the textbox behind the keyboard.
I clicked away, then clicked the textbox again, and tried typing. This time the textbox stayed higher than the keyboard. It worked just fine from then onwards. I reloaded the app and tried again, and oops, it failed the first time, but then it worked just fine the following times.
I repeated the test several times, disabled the scaling from react-native-size-matters
to make sure it's not affecting anything, but same result. First key press on phone keyboard would expand the view behind the keyboard, but after hiding and showing the keyboard, pressing keys doesn't cause any harm.
Investigation
I looked at the source code of the Spacer library, which was super awesome BTW, simplicity is great, and not easy, and I was able to fix the above problem by changing a few methods:
_keyboardWillShow = ev => {
if (this.props.enabled) {
// Calculation new position above the keyboard
let toValue = (this._locationY + this._viewHeight) - (windowHeight - (ev.endCoordinates.height + this._spaceMargin));
this._animate(-1 * toValue).start();
}
};
_setLayoutProps = ev => {
this._container
._component
.measureInWindow((x, y, width, height) => {
this._locationY = y;
this._viewHeight = height;
})
};
Both changes seemed to be needed. The first change in _keyboardWillShow
removes the dependency on this._container
in calculations. I'm not sure if this can be a problem sometimes (when the component is not outermost), which is part of the reason why I'm not creating a PR for this yet.
The other change felt more straightforward, and almost opposite the first one. It updates this._viewHeight
based on the this._container._component.measureInWindow()
.
Extras
I also added a componentWillUnmount
, but just because it felt right, not because of this issue.
It looked like:
componentWillUnmount() {
Keyboard.removeListener(showListenerEvent, this._keyboardWillShow);
Keyboard.removeListener(hideListenerEvent, this._keyboardWillHide);
}