TabBarIOS on React Native not working as expected - react-native

I'm having problems on implementing Tab Bar for React Native. The documentation does not exist (http://facebook.github.io/react-native/docs/tabbarios.html) and the example at their front page is insufficient (e.g. missing required property icon).
I managed to implement it from code point-of-view and something showed up. But only a light blue box taking half of the space on the screen.
My "working" code looked like this:
<TabBarIOS>
<TabBarIOS.Item title="Wooden" selected={false} icon={require('image!wooden')}>
<NavigatorIOS initialRoute={{ title: 'Wooden' }} />
</TabBarIOS.Item>
</TabBarIOS>
But like said, the end result was not expected.
Has anyone managed to implement TabBarIOS successfully? I tried to search through source code but there were no gotchas that would've helped me.

Answering to my own question, this is how I got it working:
First we need to define TabBarItemIOS:
var React = require('react-native');
var {
Image,
StyleSheet,
Text,
View,
TabBarIOS
} = React;
var TabBarItemIOS = TabBarIOS.Item;
Then, we can use a helper for defining icons:
function _icon(imageUri) {
return {
uri: imageUri,
isStatic: true
};
}
And, well... the rest of the actual code:
<TabBarIOS>
<TabBarItemIOS
icon={_icon('favorites')}>
</TabBarItemIOS>
<TabBarItemIOS
icon={_icon('history')}>
</TabBarItemIOS>
<TabBarItemIOS
icon={_icon('more')}>
</TabBarItemIOS>
</TabBarIOS>
This returns at least basic kind of TabBar.
This is based on the example which can be found from here: https://github.com/facebook/react-native/blob/master/Examples/UIExplorer/TabBarExample.js

I think you were already on the right track with your first post. You need to use the right resolutions for your icons. More here: Apple iOS Docs
There needs to be 3 resolutions for the same icon, e.g 32x32 = #1, 64x64 = #2 and 92x92 = #3.
Remember to follow the instructions how to create image assets in the React Native Docs for Static Resources. One image resource needs to have the same name as the image asset in Xcode.
And maybe your image doesn't have transparent borders like this case.
Here is my working code:
...
var homeIconUnactive = require('image!home-unactive');
var homeIconActive = require('image!home-active');
...
<TabBarIOS.Item
title='Home'
selected={this.state.selectedTab === 'EventList'}
icon={homeIconUnactive}
selectedIcon={homeIconActive}
onPress={() => {
this.setState({
selectedTab: 'EventList',
});
}}>
<EventList/>
</TabBarIOS.Item>
My tab icons are still blue when they are selected though...

When I tried this, the "TabBarItemIOS" seems to crash with an error 'Invariant Violation: onlyChild must be passed a children with exactly one child.' when the nested component a "NavigatorIOS" like in your example. It seems to work when child is a "View" component though. Did you get your code working?

Not sure exactly what you are trying to do. To get the tabBarIOS to work you need to as you say start with
var {
AppRegistry,
StyleSheet,
Text,
View,
NavigatorIOS,
TabBarIOS,
} = React;
Then create your class. Then create your constructor which starts which tab you want to be selected then you need to make methods that change the selected tab - when a tab is selected it is blue. then your render return with each TabBarIOS, inside each TabBarIOS.item, you must call the page you want it to go to
class example extends React.Component{
constructor(props){
super(props)
this.state = {
selectedTab: 'sassi',
}
}
homeHandleChange(){
this.setState({
selectedTab: 'home',
})
};
aboutHandleChange(){
this.setState({
selectedTab: 'about',
})
};
creditHandleChange(){
this.setState({
selectedTab: 'credits',
})
};
render() {
return (
<View style={styles.container}>
<View style={styles.footer}>
<TabBarIOS>
<TabBarIOS.Item
title="home List"
selected={this.state.selectedTab === "home"}
icon={require("./App/assets/youricon.png")}
onPress={this.homeHandleChange.bind(this)} >
<View style={styles.main}>
<home></home>
</View>
</TabBarIOS.Item>
<TabBarIOS.Item
title="credits"
selected={this.state.selectedTab === "credits"}
icon={require("./App/assets/yourIcon.png")}
onPress={this.creditsHandleChange.bind(this)} >
<View style={styles.main}>
<credits></credits>
</View>
</TabBarIOS.Item>
<TabBarIOS.Item
title="About"
selected={this.state.selectedTab === "about"}
icon={require("./App/assets/aboutIcon.png")}
onPress={this.aboutHandleChange.bind(this)} >
<View style={styles.main}>
<About></About>
</View>
</TabBarIOS.Item>
</TabBarIOS>
</View>
</View>
);
}
};

I got the same issue. But yes there are example from the UIExplorer that show the basic usage of icon. But unfortunately there's only 12 default system icons available now. Source code here: https://github.com/facebook/react-native/blob/master/React/Views/RCTTabBarItem.m#L28
I'm not quite familiar with object-c code so I'm still confused on how to set custom icon. But you can leave it like this for now(hope someone could get a better solution soon):
<TabBarIOS
selectedTab={this.state.selectedTab}>
<TabBarItemIOS
accessibilityLabel="Schedule"
title="Schedule"
name="scheduleTab"
icon={{}}
selected={this.state.selectedTab === 'scheduleTab'}
onPress={() => {
this.setState({
selectedTab: 'scheduleTab'
});
}}>
<NavigatorIOS
style={styles.container}
initialRoute={{
title: 'Schedule',
component: SchedulePage,
}}
/>
</TabBarItemIOS>
</TabBarIOS>

Related

react native textinput lost focus after 1 char type

I have this problem with ios but not with android. It only disturb the add task input the task edit and the list name edit. The input addList(It's the one with "What to do?" on the draw) in the header works fine.
UI drawing
Achitecture of components
I console log my component and I can see it rerender everytime I add a letter in the input field.
I checked on google and follow this:(can we link other website here?) https://www.codegrepper.com/code-examples/javascript/react+native+textinput+lost+focus+after+charter+type
Tried the the first solution with onBlurr and onFocus.
I tried to make a TextInput component for add task.
I even try with my component addList but it didn't solve the problem.
Anyone have faced this problem before? Is there anyway to by pass this?
My code without the import/style look like this:
const TaskList: FunctionComponent<TasksListProps> = ({
addTask,
deleteTask,
toggleTask,
editTaskName,
...props
}) => {
console.log('props', props);
const [nameOfTask, setNameOfTask] = useState('');
console.log('name', nameOfTask);
const textHandler = (enteredName: string) => {
setNameOfTask(enteredName);
};
const handleSubmitTask = () => {
if (nameOfTask === '') {
return;
}
addTask(props.listId, nameOfTask);
setNameOfTask('');
};
return (
<View style={styles.tasksListContainer}>
{props.tasks.map(task => (
<SingleTask
key={task.id}
task={task}
listId={props.listId}
deleteTask={deleteTask}
toggleTask={toggleTask}
editTaskName={editTaskName}
/>
))}
<View style={styles.taskInputContainer}>
<TextInput
style={styles.tasksTextInput}
value={nameOfTask}
onChangeText={textHandler}
placeholder="Write a task to do"
/>
<TouchableOpacity onPress={handleSubmitTask}>
<Image source={require('./Img/add-button.png')} />
</TouchableOpacity>
</View>
</View>
);
};
You can create a HOC and wrap your screen width DismissKeyboard
import { Keyboard } from 'react-native';
const DismissKeyboard = ({ children }) => (
<TouchableWithoutFeedback onPress={() => Keyboard.dismiss()}>
{children}
</TouchableWithoutFeedback>
);
That because Re render.
Try to make the input with the main component of the page to test it.
Then check where the error with re-render

DoneButton is not rendering in 'react-native-app-intro-slider-rerender-on-prop-change'

I am trying to make a multiple choice test with a few questions using React Native package 'react-native-app-intro-slider'. The problem was that this package was not rerendering the items when something changes, state for example. I found a similar or refactored package called 'react-native-app-intro-slider-rerender-on-prop-change' that did the same thing but rerendered items on a state change. But the problem is that it does not render the Done button at last slide.
The last package i used was this :
import AppIntroSlider from 'react-native-app-intro-slider-rerender-on-prop-change';
The render method for the 'MCTContainer' (Multiple Choice Container) class is :
render() {
if (this.state.showRealApp) {
this.props.navigation.navigate('TopicGroupDetails', { topicGroup: this.state.topicGroup });
return null;
} else {
const questions = this.state.questions;
return (
<AppIntroSlider
renderItem={this._renderItem}
slides={questions}
onDone={this._onDone}
onSkip={this._onDone}
activeDotStyle={{ backgroundColor: '#039BE5' }}
showPrevButton
showNextButton
showSkipButton
showDoneButton
renderPrevButton={this._renderPrevButton}
renderNextButton={this._renderNextButton}
renderSkipButton={this._renderSkipButton}
renderDoneButton={this._renderDoneButton}
/>
)
}
}
and its renderDoneButton method is like following :
_renderDoneButton = () => {
return (
<View style={styles.buttonCircle}>
<Icon
name="check"
color="#039BE5"
size={24}
style={{ backgroundColor: 'transparent' }}
/>
</View>
);
};
This is actually the same, exactly the same as I used for the component 'SliderComponent' where I show tutorial introductory text in multiple slides. It renders the 'done' button fine. But the only differenence is that I used package 'react-native-app-intro-slider' there, not 'react-native-app-intro-slider-rerender-on-prop-change'.
Please help me if someone has encountered such a problem. Thanx in advance ...
I finally solved it.
I just added a condition on "renderItem" function of the "AppIntroSlider", that checks whether it is the last slide. If it is it renders a button that calls "AppIntroSlider"-s "_onDone" method that completes the slide show.
_renderItem = ({item}) => {
return(
{
(this.state.isLastSlide) ?
(
<TouchableHighlight
onPress={this._onDone}
>
<View>
<Text>
COMPLETE SLIDE SHOW
</Text>
<Icon
name="check"
color="white"
size={24}
/>
</View>
</TouchableHighlight>
) :
(<View></View>)
}
)
}

How to hide the statusBar when react-native modal shown?

I want to hide the status bar, when modal window is shown.
My setup is as following, but it won't work as expected:
<StatusBar animated={true} hidden={true} translucent={true}>
Use statusBarTranslucent
If your status bar is translucent, you can set statusBarTranslucent to the modal.
Added since React Native 0.62
<Modal {...props} statusBarTranslucent>...</Modal>
Credit: https://github.com/react-native-modal/react-native-modal/issues/50#issuecomment-607535322
This is a known issue and there seems to be no official/React way to fix it yet. You can follow the discussion here:
https://github.com/facebook/react-native/issues/7474
I saw a post in this discussion which proposes a hack to hide it, but I haven't tried it on my project. You can also upvote this trick if it works for you.
<View style={styles.outerContainer}
<View style={styles.container}>
<StatusBar hidden={true}/>
<View style={styles.content}>
</View>
<Modal animation={fade} transparent={true}>
{/*Modal Contents Here*/}
</Modal>
</View>
A more solid fix may be changing the theme of activity in native android code.
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.ReactNative.AppCompat.Light.NoActionBar.FullScreen">
<!-- Customize your theme here. -->
</style>
<style name="AppTheme.Launcher">
<item name="android:windowBackground">#drawable/launch_screen</item>
</style>
</resources>
Credits go to Traviskn and mbashiq who proposed fixes above. I recommend you to subscribe that issue.
According to the documentations, you should be able to hide status bar in both iOS and Android using this
import {StatusBar} from 'react-native';
StatusBar.setHidden(true);
We can use the background of StatusBar to solve this problem easily but may not the best.
<Modal transparent>
{Platform.OS === 'android' ?
<StatusBar backgroundColor="rgba(0,0,0,0.5)"/>
: null
}
<View style={{backgroundColor: 'rgba(0,0,0,0.5)'}}>
// ModalContent here
</View>
</Modal>
Just use the same background and this problem can be solved.
I am actually facing the same issue for some time, I tried many solutions but I didn't get rid of this problem. I also tried to use native Android code to hide the StatusBar for a single component it works in other component but when I use it in modal it just not working. So, at last, I got a solution that works for me. I remove the modal view and replace it with react-navigation to navigate to a specific path and handle the back button using BackHandler component.
i achieve this creating a custom status bar component with a modal prop:
import React from 'react'
import { StatusBar } from 'react-native'
const MyStatusBar = (props) => {
const { backgroundColor } = props
const { barStyle } = props
const { translucent } = props
const { hidden } = props
const { showHideTransition } = props
const { modal } = props;
(modal !== undefined) ? StatusBar.setHidden(true) : StatusBar.setHidden(false)
return (
<StatusBar showHideTransition={showHideTransition} hidden={hidden} translucent={translucent} backgroundColor={backgroundColor} barStyle={barStyle} />
)
}
export default MyStatusBar
inside my base component modal prop is undefined so custom status bar is shown:
<MyStatusBar backgroundColor={theme.colors.primary} barStyle={'light-content'} />
and then calling inside the component who call the modal:
<MyStatusBar modal={modalVisible ? true : undefined} />
I think the root of my problem is the same, but it appeared a little different than how it is described above.
Expected behaviour: When the Modal becomes visible the StatusBar should hide.
const [showModal, setShowModal] = useState(false)
...
<Modal
visible={showModal}
>
<StatusBar hidden={showModal} />
...
Actual bahviour: Sometimes the StatusBardissapears as expected, other times just the StatusBar background color goes away and the actual StatusBar remains.
Workaround: Due to the flickering behaviour I think the problem is a racing condition of the native Android dialog. Therefore, I built a custom Modal component that uses the StatusBar imperative api to make sure the StatusBar hide call is made before the Modal appears. So far the Problem has not reappeared.
Here is the custom Modal component:
const Modal = ({ visible, children, ...rest }) => {
const [modalVisibility, setModalVisibility] = useState(false);
useEffect(() => {
if (visible) {
StatusBar.setHidden(true);
setModalVisibility(true);
} else {
StatusBar.setHidden(false);
setModalVisibility(false);
}
}, [visible]);
return (
<RNModal
visible={modalVisibility}
{...rest}
>
{children}
</RNModal>
);
};
export default Modal;
Hello you can try this
<View style={styles.outerContainer}
<View style={styles.container}>
<StatusBar hidden={true}/>
<View style={styles.content}>
</View>
<Modal animation={fade} transparent={true}>
{/* Contents Here*/}
</Modal>
</View>
<StatusBar backgroundColor={'transparent'} translucent={true} />

How do I drop a pin at location on React Native MapView press?

https://facebook.github.io/react-native/docs/mapview.html
I am looking for an example on how to press on the map to create an annotation that location.
I know how to add the annotation, but do not know how to get the coordinates of where I just pressed.
I can't see anything about an onPress event in the react-native MapView docs or code - But found this on github: https://github.com/lelandrichardson/react-native-maps - looks pretty good and has an onPress event which returns the coordinates (https://github.com/lelandrichardson/react-native-maps#mapview-events)
Hope this helps!
Have a look into Gesture Responder System docs.
The easiest way is to wrap something into for example <TouchableOpacity /> and you can grab the coordinates in the onPress event handler:
<TouchableOpacity onPress={(event) => console.log(event.nativeEvent.locationX)}>
{/* ... */}
</TouchableOpacity>
Everything is quite nicely explained in the docs above, for more advanced gestures handling look into PanResponder.
I use the onPress method on the MapView like this:
class DefaultMarkers extends React.Component {
constructor(props) {
super(props);
this.state = {
markers: [],
};
}
onMapPress(e) {
this.setState({
markers: [
...this.state.markers,
{
coordinate: e.nativeEvent.coordinate,
key: id++,
},
],
}
);
}
render() {
return (
<View style={styles.container}>
<MapView
style={styles.map}
onPress={e => this.onMapPress(e)}
>
{this.state.markers.map(marker => (
<Marker
key={marker.key}
coordinate={marker.coordinate}
/>
))}
</MapView>
</View>
);
}
}
Check out the full example full example on the react-native-maps github

react native TabBarIOS.Item

I am using TabBarIOS.Item which has three options that each lead to a NavigatorIOS, I want to use the TabBar's so that when you click them it takes you to the first page of the NavigatorIOS as apposed to the last one before the user changed tabs, is that possible?
Thanks, Adam
So the answer is, is that TabBARIOS.item is an object and navigatorIOS is also an object, so you can give them both a ref. so they look like this.
<TabBarIOS.Item
title="partners"
selected={this.state.selectedTab === "Partners"}
icon={require("./App/assets/partnersIcon.png")}
onPress={this.partnersHandleChange.bind(this)} >
<View style={styles.main}>
<NavigatePartners ref="partners"></NavigatePartners>
</View>
</TabBarIOS.Item>
And the navigator looks like this
turn (
<NavigatorIOS
ref="navigator"
style={styles.mainContainer}
initialRoute={{
title: 'Partners',
component: Partners,
backButtonTitle: 'Back',
}}/>
Then you change the onclick to look like this
partnersHandleChange(){
if (this.state.selectedTab == "Partners")
{
this.refs.partners.refs.navigator.popToTop();
}
this.setState({
selectedTab: 'Partners',
})
};