45

I have two pickers on my screen. Whenever I navigate to the screen in iOS app I find that the pickers are always open and all options are visible.

enter image description here

It works perfectly fine in Android where the options are visible only after we click on the picker.

Can somebody suggest a solution to fix this in iOS?

Abhishek Nalin
  • 4,309
  • 5
  • 24
  • 32

8 Answers8

27

Use ActionSheet instead of Picker on iOS. https://facebook.github.io/react-native/docs/actionsheetios

As answered by jevakallio this is the default behaviour on iOS. But this doesn't give a good UX so remove all picker components and replace with ActionSheet.

I did and it works great. The reason I preferred ActionSheet over other components suggested by jevakallio because it is developed by the RN team and has a good native feeling. The last option suggested react-native-modal-picker is also very good.

enter image description here

Max Hay
  • 230
  • 2
  • 11
Abhishek Nalin
  • 4,309
  • 5
  • 24
  • 32
  • 12
    ActionSheet shouldn't be reserved for actions? – Made in Moon Mar 05 '18 at 00:58
  • You can use https://github.com/peacechen/react-native-modal-selector also. It won't give a native picker but would give a consistent feeling on both the platform. – Piyush Beli Jun 23 '19 at 07:05
  • Okay, but what about a Picker with dozens of options (like language selection)? This is not a safe drop in replacement. This is a completely different component. – EntangledLoops Sep 18 '22 at 01:31
13

That's just how the iOS UIPickerView component works - there's no way to customize it.

If you want a different kind of UI element, you'll need to write your own, or use one of the many open source libraries, such as:

Googling with these, and similar keywords, yields many other libraries as well.

jevakallio
  • 35,324
  • 3
  • 105
  • 112
  • 1
    Useful. but using action sheet for a long list of data cannot be preferred. And in react-native-modal-picker/ react-native-modal-selector there remains an extra padding at the bottom. Could nt find a perfect solution for this yet. – AFLAH ALI May 10 '19 at 05:51
12

I don't know why you'd choose the answer with ActionSheet as accepted answer. However I'll give a workaround for this problem:

Put this values in your state:

this.state= {
    pickerOpacity: 0,
    opacityOfOtherItems: 1 //THIS IS THE OPACITY OF ALL OTHER ITEMS, WHICH COLLIDES WITH YOUR PICKER.
    label: 'Firstvalue'
}

In your render method do following:

{this.checkIfIOS()}
      <Picker
         selectedValue={this.state.selected}
         style={{ height: 50, alignSelf: 'center', opacity: this.state.pickerOpacity, marginBottom:30, width: 250}}
         onValueChange={(itemValue, itemIndex) =>{
            this.setState({
                selected: itemValue,
                label: itemValue
            });
            toggle();
            }
          }>
          <Picker.Item label="Your Label" value="yourValue"/>
      </Picker>

So now we've to check, whether our client is android or ios. Therefore import Platform and put the checkIfIos()-Method in your code:

import {Platform} from 'react-native'

checkIfIOS(){
        if(Platform.OS === 'ios'){ // check if ios
            console.log("IOS!!!");
            //this button will (onpress) set our picker visible
            return (<Button buttonStyle={{backgroundColor:'#D1D1D1', opacity: this.state.opacityOfOtherItems}} onPress={this.toggle()} color="#101010" title={this.state.label} onPress={this.changeOpacity}/>); 
        }else if(Platform.OS === 'android'){ //check if android
            this.setState({
                pickerOpacity: 1 //set picker opacity:1 -> picker is visible.
            });
            console.log("ANDROID!!!");
        }
    }

toggle(){
    if(Platform.OS === 'ios'){

        if(this.state.pickerOpacity == 0){
            this.setState({
                pickerOpacity: 1,
                opacityOfOtherItems: 0 // THIS WILL HIDE YOUR BUTTON!
            });
         }else{
             this.setState({
                 pickerOpacity: 0,
                 opacityOfOtherItems: 1
             });
          }
     }
}

EDIT: Screenshot with iOS (Click here for Video)

Screenshot of DatePicker with iOS

Sercan Samet Savran
  • 755
  • 1
  • 9
  • 20
  • do you maybe have an expo snack of this? I'm having a hard time implementing this and I'm not even sure if it works. – Ren Jan 17 '19 at 18:19
  • Unfortunately not. You just have to put the toggle() & checkIfIOS() methods and run the checkIfOs before the rendering your picker and use the toggle() method in your pickers onValueChange event. If you could provide your view, can help you how to implement it. – Sercan Samet Savran Jan 19 '19 at 10:27
  • 1
    @SercanSametSavran can you please provide a screen shots of the picker? How does the button looks like in the view? It appears that the implementation of this.changeOpacity() is missing – Erab BO Mar 28 '19 at 09:37
  • Can you provide us please with a screenshot on IOS ? – Ammar Ismaeel Sep 06 '19 at 08:25
  • Sorry for late answer. But I did something better and uploaded a video, where you can see the datepicker in action (ios). There you go: https://streamable.com/pzaio – Sercan Samet Savran Dec 24 '19 at 12:57
  • Share your code please. How do you actívate the picker? – Borjante Feb 21 '20 at 15:00
  • @Borjante My code is already there, you have to use the toggle()-method to activate/deactivate the picker. – Sercan Samet Savran Mar 11 '20 at 08:01
  • 1
    This is by far the best solution and should be the accepted answer. – bitsmanent Jul 09 '20 at 16:06
7

React-native-modal-picker was discontinued. react-native-modal-selector

7

use

<Picker itemStyle={{height:50}} > </Picker>

it only affect ios picker. change picker itemstyle height to your standard.

ziggybaba
  • 250
  • 3
  • 4
2

Extending the ActionSheetIOS answer, I created a custom component that is a drop-in replacement for Picker (I'm using Button from https://react-native-elements.github.io/react-native-elements/docs/overview.html):

import React from 'react';
import { ActionSheetIOS, Platform } from 'react-native';
import { Button } from 'react-native-elements';

class PickerDropDown extends React.Component {
  onIOSButton = () => {
    let options = this.props.children.map((item, i) => {
      return item.props.label;
    });
    options.push("Cancel");
    ActionSheetIOS.showActionSheetWithOptions(
      {
        options: options,
        cancelButtonIndex: options.length - 1,
      },
      this.onIOSButtonPick
    );
  }

  onIOSButtonPick = (buttonIndex) => {
    if (buttonIndex < this.props.children.length && buttonIndex != this.props.selectedValue) {
      if (typeof this.props.selectedValue === 'undefined' || (typeof this.props.selectedValue !== 'undefined' && buttonIndex != this.findIndexForValue(this.props.selectedValue))) {
        this.props.onValueChange(this.props.children[buttonIndex].props.value, buttonIndex);
      }
    }
  }

  findLabelForValue = (searchValue) => {
    for (let i = 0; i < this.props.children.length; i++) {
      if (this.props.children[i].props.value == searchValue) {
        return this.props.children[i].props.label;
      }
    }
    return null;
  }

  findIndexForValue = (searchValue) => {
    for (let i = 0; i < this.props.children.length; i++) {
      if (this.props.children[i].props.value == searchValue) {
        return i;
      }
    }
    return -1;
  }

  render() {
    if (Platform.OS === "ios") {
      let title = "";
      if (this.props.children && this.props.children.length > 0) {
        if (typeof this.props.selectedValue !== 'undefined') {
          title = this.findLabelForValue(this.props.selectedValue);
        } else {
          title = this.props.children[0].props.label;
        }
      }
      return (
      <View>
        <Button
          title={title}
          onPress={this.onIOSButton}
          type="clear"
          icon={{
            name: "arrow-drop-down",
            size: 15,
            color: "black"
          }}
          iconRight={true}
        />
     </View>
      );
    } else {
      return (
        <Picker {...this.props} />
      );
    }
  }
}
IsmailS
  • 10,797
  • 21
  • 82
  • 134
Kevin
  • 266
  • 3
  • 16
  • What does it look like? a video or snapshot please – IsmailS Jan 14 '20 at 13:52
  • I used this and worked. Just that it is not using full available width like Picker – IsmailS Jan 14 '20 at 15:54
  • 1
    I wrapped it in a `` and set the style prop on it. Styles on `react-native-elements` button were not applying. – IsmailS Jan 14 '20 at 18:08
  • 1
    Best answer. This solution uses native picker as much possible with little code. And the dropin replacement is quick to implement. – IsmailS Jan 16 '20 at 19:09
  • 1
    I used this class to great effect, with some small changes for styling, wrapped it in a `` as suggested by @IsmailS, and published it to a Gist: https://gist.github.com/thargenediad/7615e281baab25733ca0e7f5c7cb97d6 I hope it helps someone save time trying to style Kevin's component. Enjoy! – thargenediad Mar 24 '20 at 04:10
1

react-native-dropdown has reference bugs If you don't want to use Action Sheets but a 'real' picker, this one I found is way better...

https://github.com/lawnstarter/react-native-picker-select

xerp
  • 51
  • 3
-4

import from native-base library instead of react-native

Ravinder Kumar
  • 7,407
  • 3
  • 28
  • 54