diff --git a/package.json b/package.json index 77700d93..22d9e85b 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "lodash.isequal": "^4.5.0" }, "devDependencies": { + "@react-native-picker/picker": ">=2.1.0", "@types/react-native": "^0.60.22", "babel-jest": "^23.6.0", "babel-preset-react-native": "^4.0.1", @@ -43,7 +44,6 @@ "react": "16.6.1", "react-dom": "^16.6.1", "react-native": "0.57.7", - "@react-native-picker/picker": ">=2.1.0", "react-test-renderer": "^16.6.1" }, "peerDependencies": { diff --git a/src/index.js b/src/index.js index bcbc93e8..5861855d 100644 --- a/src/index.js +++ b/src/index.js @@ -4,6 +4,11 @@ import PropTypes from 'prop-types'; import isEqual from 'lodash.isequal'; import { Picker } from '@react-native-picker/picker'; import { defaultStyles } from './styles'; +import { Dimensions } from 'react-native'; + +// Measuring the modal before rendering is not working reliably, so we need to hardcode the height +// This height was tested thoroughly on several iPhone Models (from iPhone 8 to 14 Pro) +const IOS_MODAL_HEIGHT = 262; export default class RNPickerSelect extends PureComponent { static propTypes = { @@ -31,6 +36,8 @@ export default class RNPickerSelect extends PureComponent { onOpen: PropTypes.func, useNativeAndroidPickerStyle: PropTypes.bool, fixAndroidTouchableBug: PropTypes.bool, + scrollViewRef: PropTypes.any, + scrollViewContentOffsetY: PropTypes.number, // Custom Modal props (iOS only) doneText: PropTypes.string, @@ -137,6 +144,7 @@ export default class RNPickerSelect extends PureComponent { this.onValueChange = this.onValueChange.bind(this); this.onOrientationChange = this.onOrientationChange.bind(this); this.setInputRef = this.setInputRef.bind(this); + this.scrollToInput = this.scrollToInput.bind(this); this.togglePicker = this.togglePicker.bind(this); this.renderInputAccessoryView = this.renderInputAccessoryView.bind(this); } @@ -214,12 +222,37 @@ export default class RNPickerSelect extends PureComponent { return {}; } + scrollToInput() { + if ( + this.props.scrollViewRef == null || + this.props.scrollViewContentOffsetY == null || + this.inputRef == null + ) { + return; + } + + this.inputRef.measureInWindow((_x, y, _width, height) => { + // Bottom y-position of TextInput on screen + const textInputBottomY = y + height; + // Top y-position of picker modal on screen + const modalY = Dimensions.get('window').height - IOS_MODAL_HEIGHT; + + // If TextInput is below picker modal, scroll up + if (textInputBottomY > modalY) { + this.props.scrollViewRef.current.scrollTo({ + y: textInputBottomY - modalY + this.props.scrollViewContentOffsetY, + }); + } + }); + } + triggerOpenCloseCallbacks() { const { onOpen, onClose } = this.props; const { showPicker } = this.state; if (!showPicker && onOpen) { onOpen(); + this.scrollToInput(); } if (showPicker && onClose) {