lawnstarter / react-native-picker-select

🔽 A Picker component for React Native which emulates the native <select> interfaces for iOS and Android
https://npmjs.com/package/react-native-picker-select
MIT License
1.74k stars 499 forks source link

KeyboardAvoidingView not triggered on RNPickerSelect #273

Open dquessenberry opened 4 years ago

dquessenberry commented 4 years ago

YOU MUST FILL OUT EVERY SECTION. INCOMPLETE BUG REPORTS WILL BE CLOSED.

Describe the bug
I have a list of inputs in a single ScrollView. The ScrollView is wrapped in a KeyboardAvoidingView component, however the issue still occurs without the ScrollView component as well. All TextInput components trigger the KeyboardAvoidingView properly when tapped, however, the RNPickerSelect does not. I attempted to manually set the focus of the RNPickerSelect's embedded TextInput via the onOpen callback using the TextInput ref, for which I have verified that the TextInput is in fact focused, however that still doesn't seem to trigger the KeyboardAvoidView.

To Reproduce
Steps to reproduce the behavior:

  1. Create a simple screen with multiple RNPickerSelect components inside a ScrollView component wrapped in a KeyboardAvoidingView - set KeyboardAvoidingView behavior to "padding". Note: you don't necessarily need a ScrollView as the issue still occurs without.
  2. Click on any of the RNPickerSelect component near the bottom of the device.
  3. Notice that when the picker opens it covers the field you tapped.

Expected behavior
I expect that when a RNPickerSelect component is inside a KeyboardAvoidingView and is tapped, the picker drawer opens and the screen shifts up so that the picker drawer doesn't cover the field that was tapped.

Screenshots
Below is a gif showing that the RNPickerSelect does not trigger the KeyboardAvoidingView, but a TextInput does trigger it properly. rnpickerselect_keyboardavoidingview

Additional details

Reproduction and/or code sample
I simply do not have time. I am hoping that this issue can be resolved with simple adjustments to my code and doesn't actually require code changes on the RNPickerSelect - am I just missing a certain prop?

drewpitchford commented 4 years ago

+1....this would be a great enhancement

jimjacob29 commented 3 years ago

any updates?

NathanBeesley commented 3 years ago

Have you solved this @dquessenberry? I have this working, but I used a completely different solution. I don't think I was able to get this working with this particular library as the onShow events weren't working. I can share some code if need be. Just tag me.

dquessenberry commented 3 years ago

@NathanBeesley No, I wasn't able to figure out a work around. I just didn't have the time to mess with it and left it as is. I am curious what your solution was though.

NathanBeesley commented 3 years ago

import RNPickerSelect, { PickerSelectProps } from 'react-native-picker-select' import { DeviceEventEmitter, Keyboard, LayoutRectangle, Text } from 'react-native' import InputScrollView, { InputScrollViewProps } from 'react-native-input-scroll-view' import * as _ from 'lodash'

Select Component

export const MySelect: FunctionComponent<any> = (props) => {
    const onShowKeyboard = () => {
        // Emit event that MyScrollView component listens for. LayoutRectangle is the wrapper layout of RNPickerSelect.
        _.delay(() => DeviceEventEmitter.emit('scroll_to_select', {LayoutRectangle}), 150)

        setTimeout(() => {
            // InputScrollView internally listens for this event to resize itself. It moves the bottom of the InputScrollView out of the way.
            Keyboard.emit('keyboardWillChangeFrame', {
                duration: 250,
                easing: 'keyboard',
                endCoordinates: {
                    height: 0, // {keyboardHeight} In this case its the height of the picker component
                    screenY: 0, // {windowHeight - pickerHeight} // Y posiition of where the bottom on the Scrollview will move to
                    screenX: 0,
                    width: 0
                }
            })
        }, 125)
    }

    const onHideKeyboard = () => {
        // On hide reset the keyboard to its original position
        Keyboard.emit('keyboardWillChangeFrame', {
            duration: 250,
            easing: 'keyboard',
            endCoordinates: {
                height: 0, //{keyboardHeight}, // Haven't tried 0 here.
                screenY: 0, //{windowHeight} // Height of screen
                screenX: 0,
                width: 0
            }
        })
    }

    return (
        <RNPickerSelect onOpen={onShowKeyboard} onClose={onHideKeyboard} {...props}  />
    )
}

MyScrollView

export const MyScrollView: FunctionComponent<any> = (props) => {

    const scrollRef = useRef<InputScrollView>(null)

    useEffect(() => {
        DeviceEventEmitter.addListener('scroll_to_select', scrollToSelect)
        return () => { DeviceEventEmitter.removeListener('scroll_to_select', scrollToSelect) }
    }, [])

    // Determine if we want to scroll. 
    const scrollToSelect = (rect: LayoutRectangle) => {
        if (!_.isNull(rect)) {
            // Calculate where to scroll to
            scrollRef.current.scrollTo({ x: 0, y: 0 /* {calculated position} */, animated: true })
        }
    }

    return (
        <InputScrollView ref={scrollRef} />
    )
}

This isn't a complete solution as it will probably not compile but mainly the gist of how I resolved this issue. Hope this helps.

codr36 commented 3 years ago

+1 on this

caiobiodere commented 3 years ago

@NathanBeesley got it working using your solution thanks mate!