React Native TabNavigator change TabStyle to follow according to the text - react-native

I am using expo v27.0, react native 0.55 and I as you can see in the picture that the tab have somewhat a fixed width like a default width from the tab navigation, and the text wrap into three lines, I want the text to be in 1 line and nowrap, and i have tried styling (flexWrap:
'nowrap', flex: 1) in TabStyle, LabelStyle in TabBarOptions, but still can't get the tab to have the width according to the text inside the tab.
I populate the text for the tabs dynamically from json using fetch, therefore all tabs will have different width according to the text. How to I make the tab to follow the width of the text ?
All answers are greatly welcomed.
Thank you in advance.

Solved, turns out just need to set the width to auto as follows:
tabBarOptions: {
tabStyle: {
width: 'auto'
}
}

You can use render label in render header and in that you can return your Text component and Text is having numberOfLines props that will be 1 and it will add ... at end of the text after one line.
Check example snippet:
_renderLabel = props => {
let preparedProps = {
style: {
fontFamily: fonts.Regular,
marginVertical: 8
},
fontType: props.focused ? "Medium" : "Light"
};
return (
<Text
{...preparedProps}
numberOfLines={1}
ref={ref => {
ref && this.props.addAppTourTarget(ref, props.route.key);
}}
>
{props.route.type === "free" && this.state.is_premium_member
? this.labels.premium
: props.route.title}
</Text>
);
};
_renderHeader = props => (
<TabBar
{...props}
bounces={true}
style={{
backgroundColor: colors.cardBlue
}}
indicatorStyle={{
backgroundColor: colors.radicalRed,
height: 1,
borderRightWidth: initialLayout.width * 0.1,
borderLeftWidth: initialLayout.width * 0.1,
borderColor: colors.cardBlue
}}
tabStyle={{
padding: 0,
borderTopColor: "transparent",
borderWidth: 0
}}
renderLabel={this._renderLabel}
/>
);
_handleIndexChange = index => this.setState({ index });
_renderScene = ({ route, focused }) => {
switch (route.key) {
case "a":
return <One {...this.props} route={route} focused={focused} />;
case "b":
return (
<Two {...this.props} isSeries={true} focused={focused} />
);
case "c":
return <Three {...this.props} route={route} focused={focused} />;
default:
return null;
}
};

Related

How to create a sticky tab selector with nested scrollviews like twitter or instagram profile screens [REACT-NATIVE]

I'm trying to create a ScrollView which contains one sticky selector, that allow the selection between two nested ScollViews. It's like the twitter profile screen, or the instagram screen, where you can switch between my posts and posts where I was tagged.
Now my problem actually is that this two nested ScollViews, let's say "MY POSTS" and "TAGGED" could have different sizes, but the RootScrollView consider only the biggest height of the two scrollviews, so if in the first I've 20 items, and let's say height=1000, in the second if I don't have items, or less items, I'll have an empty space y offset like the first.
I know it's not so clear, but if you open instagram or twitter profile screens you'll immediately get it, the problem of the different heights.
Now as you'll see, what I've tried to do is create a RootScrollView, put inside it two views, the header and the sticky selector, in twitter it's the "Tweet", "Tweets and replies" ... , and the a NestedScrollView which initially has scrollEnabled=false, and then, by scroll the root I'll update it to true and to false the root one. But it seems not to work correctly.
Here's the code:
const HEADER_HEIGHT = height / 3;
const STIKY_SELECTOR_HEIGHT = 100;
const App = () => {
const rootScrollRef = useRef();
const nestedScrollRef = useRef();
const [offset, setOffset] = useState(0);
const [scrollEnabled, setScrollEnabled] = useState(false);
const onRootScroll = ({
nativeEvent: {
contentOffset: { y },
},
}) => {
const direction = y > offset ? "down" : "up";
setOffset(y);
if (y > HEADER_HEIGHT - 10 && direction == "down") {
setScrollEnabled(true);
}
};
const onNestedScroll = ({
nativeEvent: {
contentOffset: { y },
},
}) => {
if (y < 20) setScrollEnabled(false);
};
const renderItem = () => {
return <View style={styles.cell} />;
};
return (
<View style={{ flex: 1 }}>
{/* ROOT SCROLLVIEW */}
<ScrollView
simultaneousHandlers={nestedScrollRef}
scrollEventThrottle={16}
ref={rootScrollRef}
onScroll={onRootScroll}
stickyHeaderIndices={[1]}
scrollEnabled={!scrollEnabled}
style={{ flex: 1, backgroundColor: "gray" }}
>
{/* HEADER */}
<View
style={{ width, height: HEADER_HEIGHT, backgroundColor: "darkblue" }}
></View>
{/* STIKY SELECTOR VIEW */}
<View
style={{ height: STIKY_SELECTOR_HEIGHT, backgroundColor: "red" }}
></View>
{/* NESTED SCROLLVIEW */}
<View style={{ height: height - STIKY_SELECTOR_HEIGHT }}>
<FlatList
data={[1, 2, 3, 4, 5, 6, 7]}
ref={nestedScrollRef}
scrollEventThrottle={16}
onScroll={onNestedScroll}
scrollEnabled={scrollEnabled}
renderItem={renderItem}
numColumns={2}
contentContainerStyle={{
justifyContent: "space-between",
}}
/>
</View>
</ScrollView>
</View>
);
};
If someone is facing the same problem there a component for that react-native-collapsible-tab-view
<Tabs.Container
renderHeader={Header}
headerHeight={HEADER_HEIGHT} // optional>
<Tabs.Tab name="A">
<Tabs.FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={identity}
/>
</Tabs.Tab>
<Tabs.Tab name="B">
<Tabs.ScrollView>
<View style={[styles.box, styles.boxA]} />
<View style={[styles.box, styles.boxB]} />
</Tabs.ScrollView>
</Tabs.Tab>
</Tabs.Container>

React Native - unable to change font when secureTextEntry is set

const entryInput = forwardRef((props, ref) => {
return (
<View
style={{
fontFamily: "roboto-regular",
color: "rgba(255,0,0,0.6)",
fontSize: hp("1.5%")
}}>
<Text style={styles.text}>{props.show_err ? props.err : null}</Text>
<TextInput
ref={ref}
style={{
borderColor:
!props.err || props.err === "" || props.err === props.empty_err
? "gray"
: "rgba(255,0,0,0.6)",
backgroundColor: "rgba(213, 213, 213, 0.1)",
borderWidth: wp("0.3%"),
borderRadius: wp("1%"),
width: wp("85%"),
height: hp("5.2%"),
fontFamily: "roboto-regular",
fontSize: hp("2%"),
fontWeight: "normal"
}}
returnKeyType={props.last ? "done" : "next"}
blurOnSubmit={props.last ? true : false}
placeholderTextColor={"gray"}
paddingLeft={wp("2%")}
paddingRight={hp("2%")}
placeholder={props.placeholder}
onSubmitEditing={() => {
if (props.next_input) {
props.next_input.current.focus();
} else if (props.action) {
props.action();
}
}}
onChangeText={(text) => {
if (props.setText) props.setText(text);
if (props.validate) props.validate(text);
}}
/>
</View>
);});
New to react native... trying to create an input field for a password.
This custom component works great, but when I add the secureTextEntry={true} the font changes for no reason (it's not roboto-regular), it doesn't even change to the default font.
I noticed that when I remove the fontFamily key from the style object then save my code and the expo client reloads, then add fontFamily again and reload again the TextInput behaves as expected and the font is the one I set (roboto-regular), however the bug reappears when manually reloading the app.
The accepted answer will work on luck. The refs can be both defined or undefined when the component is being mounted.
Add the following to your component to properly solve the issue:
const _inputRef = useRef(null);
const setRef = useCallback((node) => {
if (_inputRef.current) {
// Make sure to cleanup any events/references added to the last instance
}
if (node) {
// Check if a node is actually passed. Otherwise node would be null.
// You can now do what you need to, setNativeProps, addEventListeners, measure, etc.
node.setNativeProps({
style: { fontFamily: "Quicksand-Medium" },
});
}
// Save a reference to the node
_inputRef.current = node;
}, []);
Make sure your TextInput has this ref assigned:
<TextInput ref={setRef} ... />
Adding the following to my custom component fixed the problem:
useEffect(() => {
if (ref) {
ref.current.setNativeProps({
style: { fontFamily: "roboto-regular" }
});
}
}, []);

Navigation using images nested inside Touchable Opacity

Background:
I've designed a custom footer for my app in React Native, I've set some images to act as icons. I'm trying to have them redirect to other pages of the app upon touch.
What I have tried
I've been trying to use the same images nested within TouchableOpacity components to have them redirect to other pages using react navigation.
This is my code:
export class Footer extends React.Component {
render (){
return (
<View style = { styles.footStyle } >
<TouchableOpacity onPress={ () => navigation.push('Home')} >
<Image
style = { styles.iconStyle }
source = {require('./img/home.png')}/>
</TouchableOpacity>
<TouchableOpacity onPress={ () => navigation.push('Favoritos')} >
<Image
style = { styles.iconStyle }
source = {require('./img/heart.png')}/>
</TouchableOpacity>
<TouchableOpacity onPress={ () => navigation.push('Search')} >
<Image
style = { styles.iconStyle }
source = {require('./img/search.png')}/>
</TouchableOpacity>
<TouchableOpacity onPress={ () => navigation.push('Notifications')} >
<Image
style = { styles.iconStyle }
source = {require('./img/bell.png')}/>
</TouchableOpacity>
<TouchableOpacity onPress={ () => navigation.push('Help')} >
<Image
style = { styles.iconStyle }
source = {require('./img/circle.png')}/>
</TouchableOpacity>
</View>
)
}
}
const styles = StyleSheet.create({
footStyle: {
paddingBottom: 0,
paddingRight: 10,
backgroundColor: '#ffffff',
flex: 0.4,
flexDirection: 'row',
borderTopWidth: 1,
borderTopColor: '#000000'
},
iconStyle: {
flex: 0.2,
height: undefined,
width: undefined
}
})
Problem
When I try and run the app in expo, the images are not rendering at all. I get my blank footer without any content. I've tried touching the footer to see if the images weren't rendering but the "button" actually worked, that didn't work.
Question
How exactly can I nest an image within a TouchableOpacity component? Is it even possible to use this method with React Navigation?
Thanks a lot!
For an Image component to work you should provide a height and width in style.
Here you are setting it as undefined
Try something like
iconStyle: {
flex: 0.2,
height: 100,
width: 100
}
Also on the navigation, you will have to pass the navigation prop to the Footer. As its a class you should access it as this.props.navigation.navigate()
As your code for integrating the Footer is not here, its hard to comment on how to pass the prop to the footer.

React-native : show content on two rows

I want to show 6 options per row instead of showing only one option per row.
I'm still new in react-native
Here is the code :
import React from 'react';
import {fromJS} from 'immutable';
import {ScrollView, StyleSheet, TouchableOpacity} from 'react-native';
import {Text} from 'src/components';
import Option from './OptionVariable';
import {checkOption} from 'src/modules/product/helper';
import {margin} from 'src/components/config/spacing';
class AttributeVariable extends React.Component {
onSelectOption = (option) => {
const {onSelectAttribute, attribute} = this.props;
onSelectAttribute(attribute.get('id'), attribute.get('name'), option.get('option'));
};
render() {
const {attribute, meta_data, variations} = this.props;
// Attribute selected
const attributeSelected = meta_data.find(attr => attr.get('id') === attribute.get('id') && attr.get('name') === attribute.get('name'));
return (
<>
<Text>
{attribute.get('name')}: <Text colorSecondary>{attributeSelected ? attributeSelected.get('option') : ''}</Text>
</Text>
<ScrollView
style={styles.attribute}
vertical
showsHorizontalScrollIndicator={false}>
{attribute.get('options').map(option => {
const disabled = meta_data.size === 0 ? false : !checkOption(variations, meta_data, fromJS({
id: attribute.get('id'),
name: attribute.get('name'),
option: option.get('option'),
}));
return (
<TouchableOpacity
activeOpacity={disabled ? 1 : 0}
key={option}
onPress={() => disabled ? {} : this.onSelectOption(option)}
>
<Option
type={attribute.get('type')}
selected={attributeSelected && option.get('option') === attributeSelected.get('option')}
disabled={disabled}
option={option}
/>
</TouchableOpacity>
)
})}
</ScrollView>
</>
);
}
}
const styles = StyleSheet.create({
attribute: {
marginTop: margin.small,
},
});
export default AttributeVariable;
Here is how it looks :
I hope everything is clear to you.
Sorry for my bad english it's not my main language....
Thanks in advance for any help.
you can say in your attribute style:
attribute: {
marginTop: margin.small,
flexDirection: 'row'
},
so it should align in rows instead of columns
That should help you to learn more about styling components in react native: https://facebook.github.io/react-native/docs/flexbox
Try using the Flatlist (instead of ScrollView) component passing the horizontal={true} props.
Like this:
FlatList
horizontal={true}
data={attribute.option}
renderItem={({ item }) => "Pass your component"}
keyExtractor={item => item.id}
/>
UPDATE 2 :
I used View instead of ScrollView with the following styles and it worked just fine..
<View style={[styles.container, ]}
horizontal
style={{AlignContent: 'flex-start', flexDirection: 'row', alignItems: 'flex-start',}}
style={styles.attribute}
showsHorizontalScrollIndicator={false}>
UPDATE 1 :
I solved the problem using this :
container: {
marginTop: margin.small,
flexDirection: 'row-reverse',
position: 'relative',
width: 'auto',
alignSelf: 'stretch',
maxWidth: 29,
height: 29,
marginBottom: margin.big,
borderRadius: borderRadius.base,
borderWidth: 1,
},
I changed the size of option so all of them appear on the same row (not exactly what I want but it works)
If anyone have a better answer please share.

React Native Tab View : How to change text color on change of tab

I am using react-native-community/react-native-tab-view
is there any method to change the tab text color on change of tab.Right now its just lighten the text color but wanted to change it to a different color ?
To change the text color on your TabBar, it should look like this:
<TabBar
{...props}
indicatorStyle={{ backgroundColor: '#eeaf3b' }}
style={{ backgroundColor: '#282828', height: 55 }}
indicatorStyle={{ backgroundColor: '#eeaf3b', height: 5 }}
renderLabel={this.renderLabel} />
Then renderLabel function should look like this:
renderLabel = ({ route, focused, color }) => {
return (
<View>
<Text
style={[focused ? styles.activeTabTextColor : styles.tabTextColor]}
>
{route.title}
</Text>
</View>
)
}
Then your style should look like this:
const styles = StyleSheet.create({
activeTabTextColor: {
color: '#eeaf3b'
},
tabTextColor: {
color: '#ccc'
}
})
In that case, try passing a callback to the renderLabel property in your TabView like so:
_renderLabel = (scene) => {
const myStyle = { /* Defined your style here.. */ }
// grab the label from the scene. I'm not really sure
// about the structure of scene, but you can see it using console.log
const label = scene.label
return (
<Text style={myStyle}>{label}</Text>
);
}
_renderHeader = () => {
return <TabBar renderLabel={this._renderLabel} />
}