react native navigation v6 header title too long - react-native

This question has been asked before but 2 years ago, hence those answers are not working with current react navigation v6. As the title of the question says, the header title, when it is a long string, it just goes beyond the width of screen and doesn't respect headerRight component as well (can see in the image), but in iOS, same code, it automatically truncates and hides text behind headerRight. Below is the code I'm using:
useLayoutEffect(() => {
navigation.setOptions({
headerTitle: data.display_name,
headerTitleStyle: {
fontWeight: '600',
fontSize: scale(20),
color: THEME.background,
},
headerTitleAlign: 'left',
headerLeft: () => (
<TouchableOpacity
onPress={handleBack}
style={{ marginRight: 5 }}>
<Ionicon name="arrow-back" size={24} color="white" />
</TouchableOpacity>
),
headerRight: () =>
(
<TouchableOpacity
style={styles.buyBtnContainer}
onPress={handleBuy}>
<Text style={styles.buy}>BUY</Text>
</TouchableOpacity>
) : null,
});
}, [data]);
What I tried:
Tried to add width: 60% inside headerTitleStyle but looks like v6 doesn't have this property, if I do add it, it just doesn't work.
Adjusting, truncating manually characterwise:-
headerTitle: Platform.OS === 'android'
? data.display_name.length <= 20
? data.display_name
: data.display_name.substring(0, 20) + '...'
: data.display_name
but due to different screen sizes, 20 pixels for 1 phone is too less for someother phone or vice-versa, so in the end, it kinda handles poorly & looks ugly...
So is there any preferred way to handle this dynamically? like how iOS does? If so please do provide solution...
package.json:
"dependencies": {
"react": "18.0.0",
"react-native": "0.69.2",
"#react-navigation/native": "^6.0.11",
"#react-navigation/native-stack": "^6.7.0",
"#react-navigation/material-top-tabs": "^6.2.2",
},

Related

How to make headerShown option dynamic in React Native?

I'm working on a React native project, in which I use #react-navigation/bottom-tabs: v 6.3.1, for navigation control.
// Navigations.js
<Tab.Screen
name='ExerciseTabScreen'
component={ExerciseStack}
options={( {route, navigation} ) =>({
title : 'Entrenamientos',
headerStyle: {backgroundColor: '#efb810', borderBottomLeftRadius: 6, borderBottomRightRadius: 6},
headerTitleStyle: {color: 'whitesmoke'},
headerShown: 'ExerciseTabScreen' === route.name ? console.log(true) : console.log(false),
headerRight: () => (
<TouchableOpacity onPress={ () => navigation.navigate('<')}>
<Text>Home</Text>
</TouchableOpacity>
),
})}
/>
I can't access the name of the ExerciseTabScreen I swear I've spent a lot of research on it, I even tried to use a Switch and the only thing I get from the console is a True when I change to any of the routes. I already tried wrapping the route.name in square brackets:
headerShown: 'ExerciseTabScreen' === [route.name] ? console.log(true) :
console.log(false),
However I get false when changing to any of the routes. How can I dynamically access the headerShown?

Try to add vertical margin for my navigation header but not working & how to add bottom line for navigation header

I am using React Navigation v5. I try to add some vertical margins for my header. This is what I tried:
navigation.setOptions({
headerStyle: {
backgroundColor: '#f4511e',
marginVertical: 10
},
headerTitleContainerStyle: {
marginVertical: 10,
},
headerTitleStyle: {
marginVertical: 10,
},
})
I hoped at least one of the above style options can have some effect, but my header has no vertical margin still. How to add vertical margin to my navigation header?
Another question is that I would like to show a bottom line of my header, how to do that?
First, You need to increase the header height then you can add margin into the header content. Please try the following code.
this.props.navigation.setOptions({
headerStyle: {
backgroundColor: '#6ff',
height:100,
marginVertical: 10
},
headerTitleContainerStyle: {
backgroundColor:'red'
},
headerTitleStyle: {
backgroundColor:'yellow',
marginVertical: 20,
},
})
You can get default header height in React V5 by using the following code and then you can add more height as per the requirement:-
React navigation V5
import { useHeaderHeight } from '#react-navigation/stack';
const headerHeight = useHeaderHeight()+ `HeightYouWant`;
For the Bottom Line, your can use borderBottomColor and borderBottomWidth style inside headerStyle
headerStyle: {
backgroundColor: '#6ff',
height:100,
marginVertical: 10,
borderBottomColor:"#FF00FF",
borderBottomWidth:5
},
I don't think it is possible to add margin or padding to default navigation options, but there is one thing we can do, is create a custom Header component and pass to navigationOptions such as,
navigation.setOptions({
header:props => <HeaderComponent {...props} />,
})
and our Header Component
export function HeaderComponent(props) {
return(
<View style = {{
height:80,
backgroundColor: '#f4511e',
borderBottomWidth:1,
borderBottomColor:'black',
marginVertical:10,
borderBottomWidth:5,
}}>
<View style={{flex:1, justifyContent:'center'}}>
<Text style={{fontSize:20, textAlign:'center'}}>{props.scene.route.name}</Text>
</View>
</View>
)
}
Also you can use React-Native-Elements , It is easy to use highly customizeable.
https://reactnativeelements.com/docs/header

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 Tab Bar white space under tabs

I am using a tab bar navigator with SafeAreaView.
If I comment out the tab bar nav the parent view covers the entire screen. However when I add a Tab bar it shows a small white view under the tab bar section.
const App = () => {
return (
<SafeAreaView style={styles.droidSafeArea}>
<View style={{ backgroundColor: "red", flex: 1 }}>
<TabNavigator key="MainTabNav" />
</View>
</SafeAreaView>
);
};
export default App;
const styles = StyleSheet.create({
droidSafeArea: {
flex: 1,
backgroundColor: "#2F3438",
}
});
Try this
screenOptions={{
tabBarStyle: {
paddingBottom:0,
},
}}
Please use the tab bar outside the safeAreaView else the safe area view will calculate the notch and will render the tab bar above the notch.
2.If you are using tab bar inside the safe area view use the force inset property of safe area view : <SafeAreaView forceInset = {bottom : 'never} this will make the safeareaview collide with bottom area and your tab bar will render properly.
Note : by using this method you would have to be a bit accurate in providing the styles.
const App = () => {
return (
<SafeAreaView style={styles.droidSafeArea} forceInset = {bottom : 'never'}>
<View style={{ backgroundColor: "red", flex: 1 }}>
<TabNavigator key="MainTabNav" />
</View>
</SafeAreaView>
);
};
export default App;
const styles = StyleSheet.create({
droidSafeArea: {
flex: 1,
backgroundColor: "#2F3438",
}
});
I had the exact same issue and what I did is not use SafeAreaView at all around the tab bar, but simply assigning the color I want the white space to have as the background color for the tab bar.
In your example that would be:
return (
<View>
<TabNavigator style={{ backgroundColor: "#2F3438" }} key="MainTabNav" />
</View>
);
<NavigationContainer>
<Tab.Navigator
tabBarOptions={{
activeTintColor: Colors.tabIconSelected,
inactiveTintColor: Colors.tabIconDefault,
style: styles.container
}}/>
</NavigationContainer>
const styles = StyleSheet.create({
container: {
backgroundColor: Colors.darkBackgroundColor,
borderTopWidth: 0
}
});
Note : borderTopWidth: 0 worked for me
For react native navigation V5
<Tab.Navigator
tabBarOptions={{
style: {
borderTopWidth: 0
}
}}
>
<Tab.Screen/>
<Tab.Navigator>
Note: this is for bottom tab
When I was implementing floating button on bottomTabNavigation followed this post, I faced similar issue that tabBar has dirty white space with shadow(I used shadow in style of component).
I used React navigation v6.
issue image1, issue image2 (Sorry, It's my first Answer I post, I can't embed image yet)
I tried to remove it with borderWidth: 0, but not worked.
My case, below is worked for me.
Try this
borderRadius: 25 // some much number that near tabbar height
on
<Tab.Navigator
tabBar={(props) => (
<View style={styles.navigatorContainer}>
<BottomTabBar {...props} />
{isIphoneX() && (
<View
style={[
styles.xFillLine,
{ backgroundColor: "#fff" },
]}
/>
)}
</View>
)}
screenOptions={{
headerShown: false,
tabBarShowLabel: false,
tabBarStyle: {
borderRadius: 25, // add here
borderTopWidth: 0,
borderRadius: 25,
backgroundColor: "transparent",
elevation: 30,
},
tabBarItemStyle: { backgroundColor: "#fff" },
}}
>
...
Then result image is this.
I don't understand why It was worked, but I hope it works for someone.
I had this issue when i was using the TabBarIcon property within the Tab.Screen
Tab being const Tab = createBottomTabNavigator()
I couldn't solve the issue no matter how i used the SafeAreaView.
I solved it by not using the TabBarIcon property and instead making a custom component for the tabBar property on the higher level Tab.Navigator as outlined in the react native docs https://reactnavigation.org/docs/bottom-tab-navigator/
When i created the custom tabBar component it all worked as expected, no funky use of SafeAreaView.

React Native Autocomplete Input results list ignores touch

I've been tackling this issue for a few weeks, and it's driving me insane.
Basically, I have a Modal component that nests a form. The form is a bunch of TextInput components, at the heart of everything. One of the components in the form is an Autocomplete, from React Native Autocomplete Input. The problem is that I'm able to put the results list from Autocomplete in front of everything else, but my touches pass right through the container and focuses on the TextInput behind the results list. I'm not able to change the order of components, so I can't just put this one input after everything else.
The general setup of the form is below:
<Modal>
<TouchableWithoutFeedback>
<View style={containerStyle}>
<TouchableWithoutFeedback>
<View>
<CardSection style={sectionStyle}>
<Input
...props...
/>
</CardSection>
<CardSection style={acSectionStyle}>
<Text style={labelStyle}>Brand *</Text>
<View style={acContainerStyle}>
<Autocomplete
autoCapitalize='none'
autoCorrect={false}
listStyle={acListStyle}
data={brands.length === 1 && comp(query, brands[0]) ? [] : brands}
defaultValue={query}
onChangeText={text => this.setState({ query: text })}
renderItem={this.renderItem.bind(this)}
hideResults={this.state.hideResults ? this.state.hideResults : undefined}
onBlur={() => this.setState({ hideResults: true })}
onFocus={() => this.setState({ hideResults: false })}
/>
</View>
</CardSection>
<CardSection style={sectionStyle}>
<Input
...props...
/>
</CardSection>
</View>
</TouchableWithoutFeedback>
</View>
</TouchableWithoutFeedback>
</Modal>
I had to stack the TouchableWithoutFeedback components in order to make the modal behave. There's more props in the components, but I only kept what was relevant.
My renderItem method is:
renderItem(brand) {
return (
<TouchableOpacity
style={{ width: '100%', height: 25 }}
onPress={() => {
this.setState({ pBrand: brand.trim(), query: brand.trim() });
}}
>
<Text style={styles.listItemStyle}>{brand}</Text>
</TouchableOpacity>
);
}
I don't believe it's a styling issue, but I've added the styles that deal with zIndex just in case:
containerStyle: {
backgroundColor: 'rgba(0, 0, 0, 0.75)',
position: 'relative',
flex: 1,
justifyContent: 'center',
zIndex: 1
},
acSectionStyle: {
justifyContent: 'center',
zIndex: 2,
height: 40
},
acContainerStyle: {
right: 0,
width: '75%',
flex: 1,
position: 'absolute',
zIndex: 2
}
The default keyboardShouldPersistTaps for Autocomplete is always. All of the questions I've seen suggest to set a higher zIndex (which isn't a problem - I can see the list, but if I tap on it, the tap goes through to the TextInput behind it), change the order of components (which I can't do), set onStartShouldSetResponderCapture to true (which didn't work), or mess with Pointer Events, none of which worked.
I'm using React Native V0.57.1, an actual Android device, and the project is built with Expo.
Finally, I've recorded a small demo for what my problem is. When the cursor re-appears, that's when I clicked on a result.
Is there just something I'm missing? I've only been writing in React Native for a few months so that's a definite possibility. I come from a web development background, so I thought that if a component was on top (thanks to zIndex), I'd be able to tap on it and not through it by default.
Edit: While messing around, if I change acSectionStyle to a height big enough for the dropdown, then the dropdown works how it should. The issue comes in when a sibling CardSection is being covered. The other CardSection takes precedence.
So, I finally found a workaround. Whether it's correct or not, which I feel like it isn't, at least it works!
I ended up taking the Autocomplete component (along with its' view) outside of the CardSection, but leaving the label in it, like so:
<CardSection style={acSectionStyle}>
<Text style={labelStyle}>Brand *</Text>
</CardSection>
<View style={acContainerStyle}>
<Autocomplete
autoCorrect={false}
listStyle={acListStyle}
// Text input container
inputContainerStyle={acTextContainerStyle}
style={acTextStyle}
placeholder='China Glaze'
data={brands.length === 1 && comp(query, brands[0]) ? [] : brands}
defaultValue={query}
onChangeText={text => this.setState({ query: text })}
renderItem={this.renderItem.bind(this)}
hideResults={this.state.hideResults ? this.state.hideResults : undefined}
onBlur={() => this.setState({ hideResults: true })}
onFocus={() => this.setState({ hideResults: false })}
/>
</View>
Then, and this is the part I think is wrong, I just played with the absolute-positioned view until I moved it far enough down to line up next to the label:
labelStyle: {
fontSize: 18,
paddingLeft: 20,
flex: 1
},
acContainerStyle: {
right: 0,
top: 102,
width: '72%',
flex: 1,
position: 'absolute',
zIndex: 10
}
I would love if someone has a better solution, but this is the best I could come up with. I try to avoid moving views with hard coded values just for scaling purposes, but it seems like this is the only option.