-
-
Save larkintuckerllc/15644c314207df00c212ecb14b981439 to your computer and use it in GitHub Desktop.
| import { PropTypes } from 'prop-types'; | |
| import React, { Component } from 'react'; | |
| import { Animated, Dimensions, Keyboard, StyleSheet, TextInput, UIManager } from 'react-native'; | |
| const { State: TextInputState } = TextInput; | |
| export default class KeyboardShift extends Component { | |
| state = { | |
| shift: new Animated.Value(0), | |
| }; | |
| componentWillMount() { | |
| this.keyboardDidShowSub = Keyboard.addListener('keyboardDidShow', this.handleKeyboardDidShow); | |
| this.keyboardDidHideSub = Keyboard.addListener('keyboardDidHide', this.handleKeyboardDidHide); | |
| } | |
| componentWillUnmount() { | |
| this.keyboardDidShowSub.remove(); | |
| this.keyboardDidHideSub.remove(); | |
| } | |
| render() { | |
| const { children: renderProp } = this.props; | |
| const { shift } = this.state; | |
| return ( | |
| <Animated.View style={[styles.container, { transform: [{translateY: shift}] }]}> | |
| {renderProp()} | |
| </Animated.View> | |
| ); | |
| } | |
| handleKeyboardDidShow = (event) => { | |
| const { height: windowHeight } = Dimensions.get('window'); | |
| const keyboardHeight = event.endCoordinates.height; | |
| const currentlyFocusedField = TextInputState.currentlyFocusedField(); | |
| UIManager.measure(currentlyFocusedField, (originX, originY, width, height, pageX, pageY) => { | |
| const fieldHeight = height; | |
| const fieldTop = pageY; | |
| const gap = (windowHeight - keyboardHeight) - (fieldTop + fieldHeight); | |
| if (gap >= 0) { | |
| return; | |
| } | |
| Animated.timing( | |
| this.state.shift, | |
| { | |
| toValue: gap, | |
| duration: 1000, | |
| useNativeDriver: true, | |
| } | |
| ).start(); | |
| }); | |
| } | |
| handleKeyboardDidHide = () => { | |
| Animated.timing( | |
| this.state.shift, | |
| { | |
| toValue: 0, | |
| duration: 1000, | |
| useNativeDriver: true, | |
| } | |
| ).start(); | |
| } | |
| } | |
| const styles = StyleSheet.create({ | |
| container: { | |
| height: '100%', | |
| left: 0, | |
| position: 'absolute', | |
| top: 0, | |
| width: '100%' | |
| } | |
| }); | |
| KeyboardShift.propTypes = { | |
| children: PropTypes.func.isRequired, | |
| }; |
any thoughts on updating for the newly deprecated componentWillMount?
constructor(props) {
super(props);
this.keyboardDidShowSub = Keyboard.addListener('keyboardDidShow', this.handleKeyboardDidShow);
this.keyboardDidHideSub = Keyboard.addListener('keyboardDidHide', this.handleKeyboardDidHide);
}
Why is shift in state if it never changes?
Thanks for this btw. I've updated it for using with hooks and TS if anyone needs:
import React, {useEffect, useState} from "react";
import {
Animated,
Dimensions,
EmitterSubscription,
Keyboard,
KeyboardEvent,
StyleSheet,
TextInput,
UIManager,
} from "react-native";
const { State: TextInputState } = TextInput;
const handleKeyboardDidShow = (shift: Animated.Value, event: KeyboardEvent) => {
const { height: windowHeight } = Dimensions.get("window");
const keyboardHeight = event.endCoordinates.height;
const currentlyFocusedField = TextInputState.currentlyFocusedField();
UIManager.measure(currentlyFocusedField, (_, __, ___, height, ____, pageY) => {
const fieldHeight = height;
const fieldTop = pageY;
const gap = (windowHeight - keyboardHeight) - (fieldTop + fieldHeight);
if (gap >= 0) {
return;
}
Animated.timing(
shift,
{
toValue: gap,
duration: 1000,
useNativeDriver: true,
},
).start();
});
};
const handleKeyboardDidHide = (shift: Animated.Value) => {
Animated.timing(
shift,
{
toValue: 0,
duration: 1000,
useNativeDriver: true,
},
).start();
};
const KeyboardShift: React.FunctionComponent<{}> = ({children}) => {
const [keyboardDidShowSub, setKeyboardDidShowSub] = useState<EmitterSubscription>();
const [keyboardDidHideSub, setKeyboardDidHideSub] = useState<EmitterSubscription>();
const [shift] = useState(new Animated.Value(0));
useEffect(() => {
if (keyboardDidShowSub == null) {
setKeyboardDidShowSub(Keyboard.addListener("keyboardDidShow", handleKeyboardDidShow.bind(null, shift)));
}
if (keyboardDidHideSub == null) {
setKeyboardDidHideSub(Keyboard.addListener("keyboardDidHide", handleKeyboardDidHide.bind(null, shift)));
}
return () => {
keyboardDidShowSub?.remove();
keyboardDidHideSub?.remove();
};
}, [keyboardDidShowSub, keyboardDidHideSub]);
return (
<Animated.View style={[styles.container, { transform: [{translateY: shift}] }]}>
{children}
</Animated.View>
);
};
const styles = StyleSheet.create({
container: {
height: "100%",
left: 0,
position: "absolute",
top: 0,
width: "100%",
},
});
export default KeyboardShift;
hi @LyricL-Gitster thank you so much for the Hooks & TS code snippet!
The code works but there is an annoying error message every time i try to open a TextInput:
currentlyFocusedField is deprecated and will be removed in a future release. Use currentlyFocusedInput
I understand that currentlyFocusedField would return the ID of the focused TextInput. However, currentlyFocusedInput apparently returns the ref of the currently focused text field, so I can't simply replace currentlyFocusedField with currentlyFocusedInput. Is there any way for me to get the ID of the field using currentlyFocusedInput?
I read online for solutions regarding this but there seems to be none that is clean. The solutions I read involves modifying node-modules which is a very hacky way and I would like to avoid such methods.
@wilbertaristo you can replace it by grabbing the native id from the currentlyFocusedInput object:
// replace currentlyFocusedField with this:
const { _nativeTag } = TextInputState.currentlyFocusedInput();@jforaker that works! thank you so much :) btw to prevent TypeScript from giving me errors I modified the above into:
const { _nativeTag } = (TextInputState as any).currentlyFocusedInput();@wilbertaristo can u please post here updated typescript class
cause of UIManager.measure also depricated.
Can we combine this with, returnKeyType={"next"}, because it works for lower part of UI when we first close the keyboard and then focus on the lower part TextInput.