React-native-slider: Slider thumb button jumping all over the screen on iOS 13

Created on 20 Sep 2019  路  20Comments  路  Source: callstack/react-native-slider

Environment

"expo": "^31.0.0",
"react": "16.5.0",
"react-native": "https://github.com/expo/react-native/archive/sdk-31.0.0.tar.gz",
"react-navigation": "^2.14.2"

Description

I just updated my iPhone 8+ to iOS 13 and have noticed some strange new behavior with the slider. When pressed and moved it jumps erratically all over the screen from left to right, darting back and forth.

I'm not sure if this has to do with the new iOS or if this was present before, however it seemed to be working fine and as expected before the iOS update.

Below is a minimum working component where I have my slider.

NOTE: on simulator it appears to work fine, it's only on my device where I see this happening. I can't a good screen shot of it but I took a video of my screen and can supply that.

Reproducible Demo

import React from 'react';
import { Dimensions, Slider, StyleSheet, Text, View } from 'react-native';

export default class DurationSetter extends React.Component {
  constructor() {
    super();
    this.state = {
        width: Dimensions.get('window').width,
        height: Dimensions.get('window').height,
        duration: 6,
        startstoppause: "stopped",
    };
    Dimensions.addEventListener("change", (e) => {
        this.setState(e.window);
    });
  }

  _onSliderChange(value) {
    this.setState(
      { duration: value },
    )
 }

  render() {
    let rate = ( 60 / (this.state.duration * 2)).toFixed(1) // <== rounds number to .1 decimal

    let styles = StyleSheet.create({
      container: {
        width: this.state.width * .75, 
        marginTop: this.state.height * .01,
        backgroundColor: 'pink',
      },
      durationText: {
        fontSize: 20,
        marginTop: 10,
        marginBottom: 10,
      },
      rateText: {
        fontSize: 14,
        marginBottom: 5,
      },
    }); 

    if(this.state.startstoppause === "started" ) {
      return null
    }
    else {
      return (
        <View style={styles.container}>
          <Text style={styles.durationText}>Set Rate of Breath:</Text>
          <Text style={styles.rateText}> {rate} breaths/min</Text>
          <Slider
            step={2} // <== Step value of the slider
            minimumValue={3} // <== Far LEFT value
            maximumValue={9} // <== Far RIGHT value
            onValueChange={this._onSliderChange.bind(this)} // <== Callback continuously called while the user is dragging the slider
            value={this.state.duration} // <== Current value of slider
            minimumTrackTintColor={'#3a6e95'}
          />
        </View>
      )
    }
  }
}
bug report

Most helpful comment

To all the people who are still struggling with this bug, you can work around this by setting the step to 0 and round the values in the on-handlers (on iOS).
I'am using the Slide from react-native itself, but this should also work with the react-native-community version.

My working example (TypeScript, React Native 0.59.10):

import React, { useRef } from 'react';
import { Platform, Slider, SliderProps } from 'react-native';

const MySlider: React.FC<SliderProps> = (props) => {
    const { onValueChange, onSlidingComplete, step, ...sliderProps } = props;

    const sliderRef = useRef<Slider | null>(null);

    const roundValue = (value: number) => {
        if (!step) {
            return value;
        } else {
            // Dividing with 1 / step helped me to get a correctly rounded values
            // e.g: When step = 0.1 and value = 4.8
            // If I return Math.round(value / step) * step,
            // the result would be 4.800000000000001 instead of 4.8
            // This is due to the transformation between decimal and binary numbers
            const dividend = 1 / step;
            return Math.round(value / step) / dividend;
        }
    };

    const onValueChangeRound = (value: number) => {
        onValueChange && onValueChange(roundValue(value));
    };

    const onSlidingCompleteRound = (value: number) => {
        const roundedValue = roundValue(value);
        if (Platform.OS === 'ios' && step && sliderRef.current) {
            // Sets the sliderValue to rounded value, so thumb snaps in to step
            sliderRef.current.setNativeProps({ value: roundedValue });
        }
        onSlidingComplete && onSlidingComplete(roundedValue);
    };

    return (
        <Slider
            ref={sliderRef}
            step={Platform.OS === 'ios' ? 0 : props.step}
            onValueChange={onValueChangeRound}
            onSlidingComplete={onSlidingCompleteRound}
            {...sliderProps}
        />
    );
};

You can use this component like the Slider component from React Native.

It wasted much of my time, hope this helps someone. :)

All 20 comments

slider-bug

Same with current version of React (16.9.0) and React Native (0.60.5).

Edit: If someone needs a fix asap: React Native Elements' slider does not have this issue.

Same here

Same here, not as bad as the above, but it moves very awkwardly.

Hello guys, any news?

Hey, I鈥檓 sorry but I cannot give you any ETA on fixing this due to very limited capacity. If there鈥檚 anyone who鈥檇 like to tackle this issue, I鈥檇 really appreciate that. Otherwise I鈥檒l try to take a look at it somewhere this month.

You can also consider react-native-slider. Seems to work fine in iOS 13.

@michalchudziak
Similar issue here, both on simulator and real device with iOS 13.
On touch start, the slider jumps back and forth until the touch stops.

"react": "16.8.6",
"react-native": "0.60.6",
"@react-native-community/slider": "2.0.1"

mee to

This PR should resolve the problem, it works like a charm for me!

Released 2.0.2 version with the fix.

Thanks @Krizzu for the contribution!

Thanks @Krizzu!!

What about for versions 1.*.*. I haven't upgraded to RN 0.60 yet. I can't use the latest release!

still having the issue
react-native version 0.61.2
slider version 2.0.2

Yes this is still a problem as the deprecated slider used in managed Expo apps has issues on iOS 13 and there is currently no way for managed Expo apps to use the react-native-community slider! And in order to get the patch for the slider to work on iOS 13, we need the ability to add that package. Any idea on when the ability to add the react-native-community-slider to managed expo apps will be integrated?

also changed over to react-native-slider 0.11.0 from the base Slider in RN. jumping issue went away. seems like a far less used Slider so would rather have more popular one avail and working in ios13. fine for now tho.

Hello there,

I use react-native v0.59 and @react-native-community/slider v1. Currently, I am not able to migrate the whole project to the latest version of React Native. Could you please make this fix for v1 too?

Thank you very much for your contribution to the open-source community 馃憤

To all the people who are still struggling with this bug, you can work around this by setting the step to 0 and round the values in the on-handlers (on iOS).
I'am using the Slide from react-native itself, but this should also work with the react-native-community version.

My working example (TypeScript, React Native 0.59.10):

import React, { useRef } from 'react';
import { Platform, Slider, SliderProps } from 'react-native';

const MySlider: React.FC<SliderProps> = (props) => {
    const { onValueChange, onSlidingComplete, step, ...sliderProps } = props;

    const sliderRef = useRef<Slider | null>(null);

    const roundValue = (value: number) => {
        if (!step) {
            return value;
        } else {
            // Dividing with 1 / step helped me to get a correctly rounded values
            // e.g: When step = 0.1 and value = 4.8
            // If I return Math.round(value / step) * step,
            // the result would be 4.800000000000001 instead of 4.8
            // This is due to the transformation between decimal and binary numbers
            const dividend = 1 / step;
            return Math.round(value / step) / dividend;
        }
    };

    const onValueChangeRound = (value: number) => {
        onValueChange && onValueChange(roundValue(value));
    };

    const onSlidingCompleteRound = (value: number) => {
        const roundedValue = roundValue(value);
        if (Platform.OS === 'ios' && step && sliderRef.current) {
            // Sets the sliderValue to rounded value, so thumb snaps in to step
            sliderRef.current.setNativeProps({ value: roundedValue });
        }
        onSlidingComplete && onSlidingComplete(roundedValue);
    };

    return (
        <Slider
            ref={sliderRef}
            step={Platform.OS === 'ios' ? 0 : props.step}
            onValueChange={onValueChangeRound}
            onSlidingComplete={onSlidingCompleteRound}
            {...sliderProps}
        />
    );
};

You can use this component like the Slider component from React Native.

It wasted much of my time, hope this helps someone. :)

@botwalker Works like a charm, thanks!

Thanks @botwalker that fixed it for me too :slightly_smiling_face:

Was this page helpful?
0 / 5 - 0 ratings

Related issues

akrger picture akrger  路  6Comments

cinder92 picture cinder92  路  4Comments

gdoudeng picture gdoudeng  路  5Comments

cpojer picture cpojer  路  6Comments

Vednus picture Vednus  路  9Comments