SegmentedControlIOS for android in react-native - react-native

I am confused with the usage of SegmentedControlIOS in react-native, i check it in IOS simulator it works, But when i check it in android it throws an error as below
SegmentedControlIOS is not supported on this platform
here is my code:
<View >
<SegmentedControlIOS
tintColor="#D7D7D5"
style={styles.SegmentedControlIOS}
values={this.state.values}
selectedIndex={this.state.selectedIndex}
onChange={this._onChange}
onValueChange={(val) =>{
this.setState({
value:val
})
}}/>
</View>
Can anyone give me suggestions on how to use SegmentedControlIOS for both android and IOS, Any help in this regard is much appreciated.

SegmentedControl is a built in native component on iOS. However, there is no direct equivalent on Android which is why the react native component name ends with IOS and isn't support on Android. There is no obvious way make the built in component work on Android.
That leaves you with two options:
Use or create your own version using standard components. This library has a good approximation of a segmented control that would work on both operating systems.
Use two separate components on iOS and Android which can be done automatically by creating two files named:componentName.android.js and componentName.ios.js (See here for more information using different code for each platform).
The iOS specific code could use the iOS segmented control and the Android version could use something like https://github.com/zzyyppqq/react-native-segmented-android or a custom implementation.

See react-native-segmented-control-tab
for similar usage between both platform:
__
-
or
see ButtonGroup from react-native-elements
https://react-native-training.github.io/react-native-elements/docs/button_group.html

Very simple component, 100% compatible with IOS version.
'use strict';
var React = require('react');
var ReactNative = require('react-native');
var { Component, View, Text, TouchableOpacity } = ReactNative;
var SimpleSegmentedControl = React.createClass({
getInitialState: function () {
return {
values: this.props.values || [],
selectedIndex: this.props.selectedIndex || 0,
style: this.props.style || {},
onChange: this.props.onChange
};
},
componentWillReceiveProps: function (props) {
this.setState(props);
},
onPress: function (selectedIndex) {
if (typeof this.state.onChange === 'function') {
return this.state.onChange(selectedIndex);
}
},
render: function () {
return (
<View style={[{flexDirection: 'row', borderWidth: 1, borderColor: '#007AFF', borderRadius: 5}, this.state.style]}>
{this.state.values.map(function (value, position, values) {
return (
<TouchableOpacity key={position} onPress={()=>this.onPress(position)} style={{flex: 1}}>
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center', padding: 5,
backgroundColor: this.state.selectedIndex == position ? '#007AFF' : 'transparent',
borderRightWidth: position < values.length - 1 ? 1 : 0, borderRightColor: '#007AFF'}}>
<Text style={{fontSize: 13, color: this.state.selectedIndex == position ? 'white' : '#007AFF'}}>{value}</Text>
</View>
</TouchableOpacity>
);
}.bind(this))}
</View>
);
}
});
module.exports = SimpleSegmentedControl;

A good equivalent for SegmentedControlIOS should be Swipe Views with tab views:
https://developer.android.com/training/implementing-navigation/lateral.html
To use it in React Native android version you can use this library: https://github.com/skv-headless/react-native-scrollable-tab-view
It's strange that React Native's team not purpose this native component built in framework

Related

Crash React Native useAnimatedKeyboard()

Hello I'm trying to use useAnimatedKayboard() hook from reanimated 2, everything works fine until I click r to reload the project. The app crashes in android and in ios. I'm using it exactly the same as the doc:
function AnimatedKeyboardExample() {
const keyboard = useAnimatedKeyboard();
const translateStyle = useAnimatedStyle(() => {
return {
transform: [{ translateY: -keyboard.height.value }],
};
});
return (
<ScrollView contentContainerStyle={{flex: 1, justifyContent: 'center',
alignItems: 'center' }}>
<Animated.View style={translateStyle}>
<TextInput />
</Animated.View>
</ScrollView>
);
}
Does anyone know how to resolve this?
EDIT: this is a new project in Expo
useAnimatedKeyboard is a Reanimated 3 feature, you're using Reanimated 2, which doesn't have it. The app crashes are most likely from calling the undefined function. If you can upgrade to Reanimated 3, that's probably your quickest solution.
The docs you linked aren't super obvious about it, but they are from the "next" version, which is 3.0. If you click through to the docs from the current release, you'll see it's not in the Hooks section.

react native snap carousel, next card preview same height

I am using react native snap carousel and I am trying to have the preview of the next card look the same height as the current card. Originally the preview is set to look centered but smaller. I would like the preview to look the same exact size.
I tried setting the containerCustomStyle to alignItems center which made it look closer to the result I wanted but the sizes are not the same. If you remove the contrainerCustomStyle you can see an exaggerated version of what I do NOT want.
I have a snack expo recreating my problem here as well as some code below. If I need to add a picture to clarify the result I would like, let me know!
I appreciate any insight at all more than you know.
renderCarouselItem = ({ item }) => {
return <View style={styles.cardContainer}>
<Text style={styles.name}>{item.name}</Text>
</View>;
};
render() {
return (
<Carousel
ref={(c) => {
this._carousel = c;
}}
data={this.state.coordinates}
renderItem={this.renderCarouselItem}
containerCustomStyle={styles.carousel}
sliderWidth={Dimensions.get('window').width}
itemWidth={300}
removeClippedSubviews={false}
/>
);
}
}
const styles = StyleSheet.create({
cardContainer: {
backgroundColor: 'red',
height: 100,
width: 300,
borderRadius: 10,
},
name: {
color: 'black',
fontSize: 22,
},
carousel: {
alignItems: 'center',
}
});
react-native-snap-carousel is deprecated. You should try react-native-reanimated-carousel

FlatList ref scrollToIndex is not a function

I am facing what seems to be a long-lasting issue in react native.
I am using Expo SDK35 with RN version 0.59. I have not updated to Expo SDK36 / RN 0.60 yet, due to large code base, but I could update if that makes up for a solution to my issue.
I have an Animated.View component that has a FlatList child, and I am unable to use the static methods (scrollToIndex() in particular) that should be available on the FlatList reference. See the next example code:
class Example extends React.Component{
constructor(props){
super(props);
this.myRef = null;
}
componentDidUpdate = () => {
/*
somewhere in code outside this class, a re-render triggers
and passes new props to this class.
I do have props change detection, and some more other code,
but I have removed it in order to minimize the code example here
*/
// This call throws:
// TypeError: undefined is not a function (near '...this._scrollRef.scrollTo...')
this.myRef.scrollToIndex({
animated: true,
index: 1,
viewOffset: 0,
viewPosition: 0.5
});
// Other suggested solution from SO
// This also throws:
// TypeError: _this.myRef.getNode is not a function. (In '_this.myRef.getNode()', '_this.myRef.getNode' is undefined)
this.myRef.getNode().scrollToIndex({
animated: true,
index: 1,
viewOffset: 0,
viewPosition: 0.5
});
}
render = () => <Animated.View style={{ /* ... some animated props */ }}>
<FlatList ref={(flatListRef) => { this.myRef = flatListRef; }}
// more FlatList related props
/>
</Animated.View>
}
I have tried to use Animated.FlatList instead, still throws the same errors as in the code example above.
I have also tried to use react native's findNodeHandle() utility function on the received flatListRef parameter, but it returns null.
I have found the same issue posted multiple times in the past here on Stack Overflow, most with no answer, or which do not work for me. These posts are also a bit old (a year or so), which is why I am posting again for the same issue.
Did anyone manage to find a solution/workaround for this issue?
EDIT: Possible workaround
As I was playing with code, I tried to use a ScrollView component instead of FlatList - and the scrollTo method works!
The changes were only on the FlatList - ScrollView specific props (so, for a ScrolLView it would be childs instead of data={[...]} and renderItem={()=>{ ... }}, ect.), and the scrollToIndex method in componentDidMount which was replaced by scrollTo.
The render method of the class, with a ScrollView, now looks like this:
render = () => <Animated.View style={{ /* ... some animated props */ }}>
<ScrollView ref={(flatListRef) => { this.myRef = flatListRef; }}>
{/*
this.renderItem is almost the same as the
renderItem method used on the FlatList
*/}
{ this.state.dataArray.map(this.renderItem) }
</ScrollView>
</Animated.View>
Please note that ScrollView does not have a scrollToIndex() method, so you'll have to cope with manually keeping track of child positions, and maybe, implement a scrollToIndex method of your own.
I am not making this the answer to my question, because the underlying issue remains. But as a workaround, maybe you can go with it and call it a day...
TL;DR;
this.myRef = React.createRef();
this.myRef.current.doSomething(); // note the use of 'current'
Long version:
While the idea behind what I was trying was correct, the error in my original post seems to be quite stupid. In my defense, the docs were not clear (probably...). Anyway...
React.createRef returns an object with a few fields on it, all of them useless for the developer (used by React in the back) - except one: current.
This prop holds the current reference to the underlying component that the ref is attached to. The main ref object is not usable for the purpose I meant to in my original question above.
Instead, this is how I should've used the ref correctly:
this.myRef.current.scrollToIndex(...)
Hold up, don't crash
Both the main myRef object, and the current field will be null if the component has not yet mounted, has unmounted at any point later, or if the ref cannot be attached to it for some reason. As you may know (or found out later), null.something will throw an error. So, to avoid it:
if ((this.myRef !== null) && (this.myRef.current !== null)){
this.myRef.current.scrollToIndex(...);
}
Extra insurance
If you try to call an undefined value as a function on a field on the ref, your code will crash. This can happend if you mistakenly reuse the same ref on multiple components, or if the component you attached it to does not have that method (i.e. View does not have a scrollTo method). To fix this you have two solutions:
// I find this to be the most elegant solution
if ((this.myRef !== null) && (this.myRef.current !== null)) {
if (typeof this.myRef.current.scrollToIndex === "function") {
this.myRef.current.scrollToIndex(...);
}
}
or
if ((this.myRef !== null) && (this.myRef.current !== null)) {
if (typeof this.myRef.current.scrollToIndex === "function") {
try {
this.myRef.current.scrollToIndex(...);
} catch (error) {
console.warn("Something went wrong", error);
}
}
}
I hope this to be useful for anyone else learning to use refs in React. Cheers :)
With Animated.ScrollView:
Create a ref to your FlatList (the old way only works):
<ScrollView ref={ (ref) => (this.MyRef=ref) } />
Access scrollToIndex using this.myRef.getNode().scrollToIndex
Animated.FlatList is currently not working unfortunately...
With FlatList:
Create a ref to your FlatList by:
<FlatList ref={ this.flatListRef } />
constructor(props) {
super(props);
this.flatListRef = React.createRef();
}
Access scrollToIndex using this.flatListRef.current.scrollToIndex
Also make sure to wrap your code inside an if statement like:
if (this.myRef.getNode()) { this.flatListRef.getNode().scrollToIndex(); }
o do not know if this will help you... it scroll to a especific item in the list:
/*Example to Scroll to a specific position in scrollview*/
import React, { Component } from 'react';
//import react in our project
import {
View,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
Image,
TextInput,
} from 'react-native';
//import all the components we needed
export default class App extends Component {
constructor() {
super();
//Array of Item to add in Scrollview
this.items = [
'zero',
'one',
'two',
'three',
'four',
'five',
'six',
'seven',
'eight',
'nine',
'ten ',
'eleven',
'twelve',
'thirteen',
'fourteen',
'fifteen',
'sixteen',
'seventeen',
'eighteen',
'nineteen',
'twenty ',
'twenty-one',
'twenty-two',
'twenty-three',
'twenty-four',
'twenty-five',
'twenty-six',
'twenty-seven',
'twenty-eight',
'twenty-nine',
'thirty',
'thirty-one',
'thirty-two',
'thirty-three',
'thirty-four',
'thirty-five',
'thirty-six',
'thirty-seven',
'thirty-eight',
'thirty-nine',
'forty',
];
//Blank array to store the location of each item
this.arr = [];
this.state = { dynamicIndex: 0 };
}
downButtonHandler = () => {
if (this.arr.length >= this.state.dynamicIndex) {
// To Scroll to the index 5 element
this.scrollview_ref.scrollTo({
x: 0,
y: this.arr[this.state.dynamicIndex],
animated: true,
});
} else {
alert('Out of Max Index');
}
};
render() {
return (
<View style={styles.container}>
<View
style={{
flexDirection: 'row',
backgroundColor: '#1e73be',
padding: 5,
}}>
<TextInput
value={String(this.state.dynamicIndex)}
numericvalue
keyboardType={'numeric'}
onChangeText={dynamicIndex => this.setState({ dynamicIndex })}
placeholder={'Enter the index to scroll'}
style={{ flex: 1, backgroundColor: 'white', padding: 10 }}
/>
<TouchableOpacity
activeOpacity={0.5}
onPress={this.downButtonHandler}
style={{ padding: 15, backgroundColor: '#f4801e' }}>
<Text style={{ color: '#fff' }}>Go to Index</Text>
</TouchableOpacity>
</View>
<ScrollView
ref={ref => {
this.scrollview_ref = ref;
}}>
{/*Loop of JS which is like foreach loop*/}
{this.items.map((item, key) => (
//key is the index of the array
//item is the single item of the array
<View
key={key}
style={styles.item}
onLayout={event => {
const layout = event.nativeEvent.layout;
this.arr[key] = layout.y;
console.log('height:', layout.height);
console.log('width:', layout.width);
console.log('x:', layout.x);
console.log('y:', layout.y);
}}>
<Text style={styles.text}>
{key}. {item}
</Text>
<View style={styles.separator} />
</View>
))}
</ScrollView>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 30,
},
separator: {
height: 1,
backgroundColor: '#707080',
width: '100%',
},
text: {
fontSize: 16,
color: '#606070',
padding: 10,
},
});
if i completly wrong, tell me...
Because ScrollView has no scrollToOffset function and It has only scrollTo function.
So let use function scrollTo with ScrollView or scrollToOffset with FlatList and it works normal.
If you are working with 'KeyboardAwareFlatList' this worked nicely:
https://github.com/APSL/react-native-keyboard-aware-scroll-view/issues/372
In short, use useRef and use the innerRef property of the KeyboardAwareFlatList rather than the ref property.

How to access camera in react native expo?

This is the code I am implementing to access camera in React Native Expo App. But this code is not working. It only shows blank screen and nothing else. Please suggest me if any changes required or any alternate method to implement this.
import React, { useState, useEffect } from 'react';
import { Text, View, TouchableOpacity } from 'react-native';
import { Camera } from 'expo-camera';
export default function App() {
const [hasPermission, setHasPermission] = useState(null);
const [type, setType] = useState(Camera.Constants.Type.back);
useEffect(() => {
(async () => {
const { status } = await Camera.requestPermissionsAsync();
setHasPermission(status === 'granted');
})();
}, []);
if (hasPermission === null) {
return <View />;
}
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}
return (
<View style={{ flex: 1 }}>
<Camera style={{ flex: 1 }} type={type}>
<View
style={{
flex: 1,
backgroundColor: 'transparent',
flexDirection: 'row',
}}>
<TouchableOpacity
style={{
flex: 0.1,
alignSelf: 'flex-end',
alignItems: 'center',
}}
onPress={() => {
setType(
type === Camera.Constants.Type.back
? Camera.Constants.Type.front
: Camera.Constants.Type.back
);
}}>
<Text style={{ fontSize: 18, marginBottom: 50, color: 'red' }}> Flip </Text>
</TouchableOpacity>
</View>
</Camera>
</View>
);
}
It looks like somehow you have denied permission in App. Also In the code, if hasPermission is null you will see a blank page.
Note: In Ios, if you have denied or granted permission once, then the app will never show permission popup again until you use linking and let the user enable permission from setting.
It looks like the Camera.requestPermissionsAsync is now deprecated. You might try using Camera.requestCameraPermissionsAsync(). After I updated my code the deprecation notice disappeared.
The Camera component shouldn't in the <SafeAreaView>
<YOUR_CAMERA_COMPONENT /> /* <-- when outside the <SafeAreaView> , it work!*/
<SafeAreaView>
<YOUR_CAMERA_COMPONENT /> /* <-- it will show blank view */
</SafeAreaView>
I solved the problem when removed the <Camera /> from <SafeAreaView />
Firstly i am using:
"expo": "~40.0.0",
now im upgrading the cli and then using expo upgrade and everything fix itself:
"expo": "^41.0.0",
"expo-camera": "~11.0.2",
"expo-cli": "^4.4.1",
"expo-image-picker": "~10.1.4",
"expo-status-bar": "~1.0.4",
"firebase": "8.2.3",
The emulator camera doesn't support this library , try using in Android or Ios device and it will work perfectly fine.
Please check this example or the original Expo-camera library.
Hope it helps .feel free for doubts
I saw your code and I feels this code same as a expo-camera documentation.
I think you are trying this code on Emulator/Simulator . please try once on
Physical Device. because emulator doesn't support camera. you can read the document in brief https://docs.expo.io/versions/latest/sdk/camera/#requestpermissionsasync

React Native <ScrollView> persistent scrollbar

After perusing the React Native Documentation I couldn't seem to find out how to make a <ScrollView> have a persistent scrollbar that doesn't fade out. How would I achieve that?
iOS
The underlying iOS native component, UIScrollView (technically, RCTEnhancedScrollView), doesn't support keeping the scroll indicators visible. For this reason, the React Native wrapper around it won't either.
There is a hack to get this working with the native component (see this answer for one approach). To accomplish this in React Native, you'd need to implement this hack on the native side, and then either create your own Native Module or fork React Native and modify their ScrollView component.
That said, the iOS Scroll View interface guidelines discourage this, so you may want to leave the indicators' behavior alone.
Android
A few approaches:
set <item name="android:overScrollMode">always</item>,
set android:fadeScrollbars="false" in XML, or
set ScrollView.setScrollbarFadingEnabled(false) in Java (e.g. in your custom native bridge code)
This is similarly discouraged as nonstandard UI unless you have a strong reason for it.
Adding answer since none of the above worked for me.
Android now has the persistentScrollbar props.
iOS does not support this. So I created a JS solution that can be used as follows:
<SBScrollView persistentScrollbar={true}>...</SBScrollView>
Basically, this functional component will use persistentScrollbar when on Android, while add a bar when we are on iOS. It is not smooth for now, but it is functional.
// #flow
import React, {useState} from 'react';
import {Platform, View, ScrollView} from 'react-native';
type Props = {|
persistentScrollbar?: boolean,
children?: React$Node,
|} & View.propTypes;
export default function SBScrollView({
persistentScrollbar = false,
children,
...other
}: Props) {
const [nativeEvent, setNativeEvent] = useState();
if (Platform.OS === 'android' || !persistentScrollbar) {
// Abdroid supports the persistentScrollbar
return (
<ScrollView persistentScrollbar={persistentScrollbar} {...other}>
{children}
</ScrollView>
);
}
const top = nativeEvent
? nativeEvent.contentOffset.y +
(nativeEvent.contentOffset.y / nativeEvent.contentSize.height) *
nativeEvent.layoutMeasurement.height
: 0;
// iOS does not support persistentScrollbar, so
// lets simulate it with a view.
return (
<ScrollView
scrollEventThrottle={5}
showsVerticalScrollIndicator={false}
onScroll={event => setNativeEvent(event.nativeEvent)}
{...other}>
{children}
<View
style={{
position: 'absolute',
top,
right: 4,
height: 200,
width: 4,
borderRadius: 20,
backgroundColor: 'gray',
}}
/>
</ScrollView>
);
}
I hope this can help others.
I was looking for a solution but I didn't find nothing, then I created a solution, I hope can help you with it.
I created a view View with height and width and put it over my scrollview, after that I used the Props of scrollview like onMomentumScrollBegin, onMomentumScrollEnd, onContentSizeChange and onScroll
after that I make a condition with a boolean variable, if this variable is false, the View is visible, if is false the View is hide, How do I active this variable? with the Prop onMomentumScrollBegin that detect when you use the scrollView and the same way to set the variable in false with onMomentumScrollEnd that detects when the scroll ends.
The Prop onContentSizeChange allows me to get the height and width of my scrollview, this values I used to calculate where would be set the scrollbar/scrollIndicator
and finally with the Prop onScroll I get the position.
the example:
<ScrollView
onMomentumScrollBegin={() => {this.setvarScrollState()}}
onMomentumScrollEnd={() => {this.setvarScrollStateRev()}}
scrollEventThrottle={5}
onContentSizeChange={(w, h) => this.state.hScroll = h}
showsVerticalScrollIndicator={false}
onScroll={event => { this.state.wScroll = event.nativeEvent.contentOffset.y }}
style={{ marginVertical: 15, marginHorizontal:15, width: this.state.measurements3.width}}>
{
Mydata.map((value, index) => {
return <TouchableOpacity>
<Text>{ value.MyDataItem }</Text>
</TouchableOpacity>
})
}
the functions:
setvarScrollState() {
this.setState({VarScroll: true});
}
setvarScrollStateRev() {
this.setState({VarScroll: false});
}
and the variable
this.state = {VarScroll: false}
Then my condition is
!this.state.VarScroll ?
<View
style = {{
marginTop: 200*(this.state.wScroll / this.state.hScroll),
marginLeft:338.5,
height: 35,
width: 2,
backgroundColor: 'grey',
position:'absolute'
}}
/>
: null
Why 200? because is the maximum value that my marginTop can set
Check the picture
Final note:
the scrollView have to be inside a View with the another View (scrollbar)
something like this
<View>
{/*---- ScrollBar and conditions----*/}
<View>
<View>
<ScrollView>
</ScrollView>
</View>