How to properly use TabNavigator inside another React Component - ios

Question - How to properly use React Navigation's TabNavigator container component inside another React component that acts just as wrapper component?
What I want to achieve - Basically I want the appbar and tabbar both to be displayed - appbar on the top, tabbar (TabBarTop) just beneath it, a very common design pattern.
I have tried a couple of ways.
Method #1 (Nesting inside StackNavigator)
Tab.js
export const Tab = TabNavigator({
Tab1: { screen: Tab1Container },
Tab2: { screen: Tab2Container }
}, {
tabBarComponent: TapBarTop,
tabBarPosition: 'top'
});
AppBar.js
class AppBarComponent extends Component {
static navigationOptions = { header: null }
render() {
return (
<View>
*some more views, buttons blah blah here
</View>
)
}
}
export default AppBarComponent;
and I use them inside StackNavigator, like
export default StackNavigator({
stack1: { screen: AppBarComponent },
stack2: { screen: Tab }
});
This results in only 1 stack to be displayed at a given time which is exactly how it works. And I don't have anything to do with initialRouteName.
Method #2 (wrapping inside another component)
<View style={{ flex: 1 }}>
<AppBarComponent />
<Tab />
</View>
This on the contrary displays both components but this.props.navigation.navigate('somepath') or push() or pop() or
replace() doesn't work from inside and . But this.props.navigation and its methods all are available inside those components.
PS - I'm using React Navigation v1 and running on iOS

Solution for Method 1
Have a StackNavigator with only one screen and show your TabNavigator in that screen. Then customize header with your custom AppBarComponent.
header
React Element or a function that given HeaderProps returns a React
Element, to display as a header. Setting to null hides header.
Sample
export default StackNavigator({
stack: {
screen: Tab,
navigationOptions: {
header: (HeaderProps) => (<AppBarComponent headerProps={HeaderProps} />)
}
}
});
Solution for Method 2
You can wrap your component which is not a part of the stack with withNavigation HOC.
withNavigation is a higher order component which passes the
navigation prop into a wrapped Component. It's useful when you cannot
pass the navigation prop into the component directly, or don't want to
pass it in case of a deeply nested child.
Sample
class AppBarComponent extends Component {
static navigationOptions = { header: null }
render() {
return (
<View>
*some more views, buttons blah blah here
</View>
)
}
}
export default withNavigation(AppBarComponent);

#bennygenel your answer is definitely useful in addressing the problem. I kind of fed wrong information when I talk about having components inside TabNavigator. Well, instead of dumb components, there were multiple redux containers inside the TabNavigator as screens and the entire TabNavigator wrapped in a Dumb component. I have edited my question sincerely.
Though this.props.navigation exists both inside the redux containers and the wrapper component, the navigation stack reference was different for them which is why methods such as navigate or goBack() were not working.
The solution was simple. Passing the navigation stack reference as screenProps to the wrapper component solves this issue.
<View style={{ flex: 1 }}>
<AppBarComponent />
<Tab screenProps={{ rootNav: this.props.navigation }} />
</View>

Related

ionic 5 Modal shows blank page on iOS

I have an issue with showing a Modal on Ionic 5 Angular 8 on iOS.
So I have a page with two buttons, one is declared in the same page component, the other one is from a child component.
When the button in the same page is clicked, the modal opens fine.
When the button from the child component is clicked, the event is emitted to the parent Page, which then creates the modal, but the modal slides up, outside the view port.
I have tried all possible trouble shooting I can think of:
Removing all custom CSS, removing all html, changing the route of the event, adding a timeout etc..
I don't have any autofocus or bootstrap: [] in my entire app.
Here is what the page looks like
export class MyPage implements OnInit, OnDestroy {
//...
private openModal (params) {
const obj = {
component: ModalComponent,
componentProps: params
};
this.modalProvider.create (obj).then (
modal => {
modal.onDidDismiss().then(
() => {}
);
modal.presente ();
}
)
}
public onOpenModal () {
const params = {
//custom properties
}
this.openModal(params);
}
public localAction () {
const params = {
// custom params
}
this.openModal (params)
}
}
Now on the template side :
<ion-content force-overscroll="false" [scrollY]="false">
<!-- Some html --->
<ion-grid (click)="localAction()">
<!-- Some html --->
</ion-grid>
<!-- Some html --->
<child-component (modalOpenEmitter)="onOpenModal($event)">
</ion-content>
Ok I have found the problem after several hours digging.
It turns out it was because of the child component which host a list of button scrolling horizontally.
When a button from the the child component was clicked, an event was emitted as per normal, but I was using a DOM function called scrollintoview like so:
Child component.ts :
public btnClicked (value) {
//...
this.clickEmitter.emit(value); // Normal stuff
const element = document.getElementById(value.id);
if (element) {
element.scrollIntoView(); // This caused the the modal to disappear
}
}
This scrollIntoView was there to make the clicked button slide further in the view port.
Unfortunately, this beaks the modal on iOS, but it works fine on android by the way.

NativeScript Button Not Working on iOS But Works on Android

I have a Github page that I will link to that has all of my code. So basically, I'm using Angular 8 along with NativeScript to build my first app. I've been following a Udemy tutorial and decided to have a series of components. A Menu component which serves as my Home routing path and a series of child routes. The Menu has a TabView which when each tab is clicked, it opens a different component. Right now, I have an About component, a Store component, and a Join component. When each child component route is opened, I wanted to have a BUTTON attach to each child route that routed to another page when clicked. So a button on the About Component that leads to an About Details component that will just have text about the company.
The problem is either with my routing or the page-router-outlet on iOS. I have an iPad and an Android phone I'm using to test things until I get a new Mac to set up the emulators. When I use the page-router-outlet in a certain location on the about-tab.component.html page, it works fine on Android and opens up the About Details component. But on iOS, nothing happens. I click the button...and nothing moves at all.
I've tried moving the page-router-outlet to different locations on the about-tab.component.html file and it either completely breaks the About tab on iOS and the screen is blank (and doesn't show anything, not even the words), or it stays the same. I created a GitHub page...
https://github.com/eoscryptodev/Best-Laces-Out
I've also tried to move around the location of the routes themselves. I tried making the About Details component a child of the About Component, which is a child of the Home component (Main). I also tried putting the component on the same level as the Home (Menu) component.
app-routing.module.ts
import { NgModule, NO_ERRORS_SCHEMA } from '#angular/core';
import { NativeScriptCommonModule } from 'nativescript-angular/common';
import { NativeScriptRouterModule } from 'nativescript-angular/router';
import { Routes } from '#angular/router';
import { MenuComponent } from './menu/menu.component';
import { AboutComponent } from './menu/about/about.component';
import { ShopComponent } from './menu/shop/shop.component';
import { JoinComponent } from './menu/join/join.component';
import { AboutDetailsComponent } from './menu/about-details/about-details.component';
//import { BottomTabComponent } from './tabs/bottom-tab/bottom-tab.component';
//import { JoinPageComponent } from './pages/join-page/join-page.component';
//FIX: You want the path to go from OurHistory(the menu option) to OutHistoryPage. Also figure out how to add the menu to the bottom of the pages.
// Maybe create an ns-path for the BottomTabBar that can be placed on HTML pages.
const routes: Routes =
[
{ path: '', redirectTo: '/home/(HistoryOutlet:OurHistory//MerchOutlet:OurMerch//ClubOutlet:OurClub)', pathMatch: 'full' },
{ path: 'home', component: MenuComponent, children:
[
{ path: 'OurHistory', component: AboutComponent, outlet: 'HistoryOutlet'},
{ path: 'HistoryDetails', component: AboutDetailsComponent, outlet: 'HistoryOutlet' },
{ path: 'OurMerch', component: ShopComponent, outlet: 'MerchOutlet' },
{ path: 'OurClub', component: JoinComponent, outlet: 'ClubOutlet' },
]}, // a root route with an empty path. The route that is loaded when our app starts. SHOULD BE LOGO IMAGE!!
]
about-tab.component.html
<ScrollView orientation="vertical" height="500">
<StackLayout orientation="vertical" backgroundColor="white">
<page-router-outlet name="HistoryOutlet"></page-router-outlet>
<Label text="This is Our History" id="ID1"></Label>
<Button height="50"
width="200"
backgroundColor="black"
text="Who We Are"
id="about-button"
[nsRouterLink]="['/home', { outlets: { HistoryOutlet:[ 'HistoryDetails' ]}} ]">
</Button>
</StackLayout>
</ScrollView>
about-details.component.html
<ns-action-bar title="Details"></ns-action-bar>
<StackLayout>
<Button
text="Go Back"
[nsRouterLink]="['/home', { outlets: { HistoryOutlet: ['OurHistory'] } } ]"
class="about-details">
</Button>
</StackLayout>
I found my solution in the NativeScript Github docs. Under TabView Navigation. I see where I went wrong. I was close, but this answers everything.
https://github.com/NativeScript/docs/blob/6ac877dc3bd551a8e338855e07597ee24358a462/docs/core-concepts/angular-navigation.md

React Native ListView refresh indicator not showing at first loading in iOS

I'm using React Native 0.33.1 and I use a ListView with Refreshcontrol to indicate that the data is being loaded. The problem is on iOS, that if the ListView datasource is empty the loading indicator is not showing, so the user doesn't see that the data is being loaded. But if the ListView is not empty everything works fine. Also there are no problems on Android even with en empty ListView.
I tried to replace the whole ListView with a Loading indicator if there is no data, but then when the data is loaded, the top of the ListView is not aligned correctly and is behind the Navigationbar of the NavigatorIOS I am using.
Here's my Code for the render method and the loading at the beginning:
render(){
return <View style={{flex:1, flexDirection:'column', backgroundColor:'#FFF'}}>
<ListView
refreshControl={ <RefreshControl refreshing={this.state.refreshing} onRefresh={this._onRefresh.bind(this)} /> }
style={{flex:1, flexDirection:'column'}}
dataSource={this.state.dataSource}
enableEmptySections={true}
renderRow={this.renderRow}
renderSectionHeader={this.renderSectionHeader}>
</ListView>
</View>
}
componentDidMount(){
this.unmounting = false;
this.loadData();
}
loadData(){
this.setState({refreshing:true});
//Code to fetch data async
.then((result) => {
this.setState({refreshing:false, result:result});
});
}

React Native Routing

How can go to the next page when I pressed a button without using the NavigatorIOS. is there a way in react native to do that?
I understand how the NavigatorIOS works but my problem is i do not want to have NavigationBar on my Login Page.
Thanks!
NavigatorIOS has a property navigationBarHidden that you could set when you are in the login screen, e.g. something like:
<NavigatorIOS
navigationBarHidden={!this.state.isLoggedIn}
//...
/>
Alternatively you could render your login screen outside the navigator hierarchy:
render: function() {
if (!this.state.isLoggedIn) {
return <LoginScreen />;
}
return (
<NavigatorIOS
//...
/>
);
},
Both of these examples presume that your button sets a state variable isLoggedIn to true.

Can React Native produce UIButton instances in Swift?

I want to use React Native to create some buttons that are wired to trigger methods in a Swift app. Is there a way to access TouchableHighlight instances from within a Swift UIViewController or ViewController class?
There are various options for buttons which you can find on npmjs.org. Below are a couple of options.
https://www.npmjs.com/package/react-native-button
https://www.npmjs.com/package/react-native-icon-button
But you won't find a UIButton instance most likely since that is iOS specific. What you get is a touchable element which acts like a button. You can inspect the code of these modules on GitHub to see how they work.
You can do this without installing additional Node modules by wrapping a <Text> component with a <View> component:
import React from 'react'
class ExampleButton extends React.Component {
constructor(props) {
super(props);
this.pressButton = this.pressButton.bind(this);
}
pressButton() {
console.log("The button was pressed!");
}
render() {
return(
<View>
<Text onPress={this.pressButton}>
Button Text
</Text>
</View>
);
}
}

Resources