Skip to content

Instantly share code, notes, and snippets.

@wandonye
Forked from fdecampredon/FormScrollView.js
Created January 23, 2017 22:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wandonye/566075b34c02be0e6d9f1f0e48bcdcf1 to your computer and use it in GitHub Desktop.
Save wandonye/566075b34c02be0e6d9f1f0e48bcdcf1 to your computer and use it in GitHub Desktop.
InputAccessoryView hack
import React, { PureComponent, PropTypes } from 'react';
import {
TouchableWithoutFeedback,
ScrollView,
View,
TextInput,
Keyboard,
findNodeHandle,
UIManager,
Dimensions,
KeyboardAvoidingView,
} from 'react-native';
class FormScrollView extends PureComponent {
static propTypes = {
keyboardOffset: PropTypes.number,
}
static childContextTypes = {
formScrollView: PropTypes.object,
}
currentInputHandle = null;
accesoryViews = []
state = {
currentAccessoryView: null,
}
getCurrentAccessoryView() {
const currentlyFocusedField = TextInput.State.currentlyFocusedField();
return this.accesoryViews
.find(viewDef => viewDef.getTextInputHandle() === currentlyFocusedField);
}
registerInputAccessoryView = (viewDef) => {
if (!this.accesoryViews.includes(viewDef)) {
this.accesoryViews.push(viewDef);
}
this.setState({
currentAccessoryView: this.getCurrentAccessoryView()
});
return {
dispose: () => {
const index = this.accesoryViews.indexOf(viewDef);
if (index !== -1) {
this.accesoryViews.splice(index, 1);
this.setState({
currentAccessoryView: this.getCurrentAccessoryView()
});
}
},
};
}
getChildContext() {
return {
formScrollView: {
registerInputAccessoryView: this.registerInputAccessoryView,
},
};
}
componentWillMount() {
this.subscriptions = [
Keyboard.addListener('keyboardWillShow', this.onKeyboardWillShow),
Keyboard.addListener('keyboardWillHide', this.onKeyboardWillHide),
];
}
componentWillUnmount() {
this.subscriptions.forEach(sub => sub.remove());
}
onKeyboardWillShow = () => {
const currentlyFocusedField = TextInput.State.currentlyFocusedField();
const scrollResponder = this.refs.scrollView.getScrollResponder();
const currentAccessoryView = this.getCurrentAccessoryView();
UIManager.viewIsDescendantOf(
currentlyFocusedField,
findNodeHandle(this.refs.scrollView),
(isAncestor) => {
const offset = (this.props.keyboardOffset || 0) +
(currentAccessoryView ? currentAccessoryView.height : 0);
if (isAncestor) {
scrollResponder.scrollResponderScrollNativeHandleToKeyboard(
currentlyFocusedField,
offset,
true
);
}
}
);
this.setState({ currentAccessoryView });
};
onKeyboardWillHide = () => {
this.setState({ currentAccessoryView: this.getCurrentAccessoryView() });
if (!this.scrolling) {
this.refs.scrollView.scrollTo({ x: 0, y: 0, animated: true });
}
};
onScroll = () => {
clearTimeout(this.timeout);
this.scrolling = true;
this.timeout = setTimeout(() => {
this.scrolling = false;
}, 100);
}
hideKeyboard = () => {
Keyboard.dismiss();
}
render() {
const { currentAccessoryView } = this.state;
const { children, ...props } = this.props;
const { width, height } = Dimensions.get('window');
return (
<View style={{ width, height }}>
<ScrollView
{...props}
ref="scrollView"
keyboardShouldPersistTaps
keyboardDismissMode="on-drag"
onScroll={this.onScroll}
scrollEventThrottle={50}
>
<TouchableWithoutFeedback onPress={this.hideKeyboard}>
<View>
{children}
</View>
</TouchableWithoutFeedback>
</ScrollView>
<KeyboardAvoidingView
pointerEvents="box-none"
behavior="position"
style={{
position: 'absolute',
top: 0,
left: 0,
height,
width,
justifyContent: 'flex-end',
}}
>
<View style={{ width }}>
{currentAccessoryView && currentAccessoryView.render()}
</View>
</KeyboardAvoidingView>
</View>
);
}
}
FormScrollView.propTypes = {
...ScrollView.propTypes,
};
export default FormScrollView;
import { PureComponent, PropTypes } from 'react';
import { findNodeHandle } from 'react-native';
class InputAccessoryView extends PureComponent {
static propTypes = {
render: PropTypes.func.isRequired,
getTextInputRef: PropTypes.func.isRequired,
height: PropTypes.number.isRequired,
}
static contextTypes = {
formScrollView: PropTypes.object,
}
getTextInputHandle = () => findNodeHandle(this.props.getTextInputRef());
componentDidMount() {
this.register();
}
componentWillReceiveProps({ render, height }) {
if (render !== this.props.render || height !== this.props.height) {
this.subscription.dispose();
this.register();
}
}
componentWillUnmount() {
this.subscription.dispose();
}
register() {
this.subscription = this.context.formScrollView.registerInputAccessoryView({
render: this.props.render,
getTextInputHandle: this.getTextInputHandle,
height: this.props.height,
});
}
render() {
return null;
}
}
export default InputAccessoryView;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment