# React Native Navigation
App-wide support for 100% native navigation with an easy cross-platform interface. For iOS, this package is a wrapper around [react-native-controllers](https://github.com/wix/react-native-controllers), but provides a simplified more abstract API over it. This abstract API will be unified with the Android solution which is currently work in progress.
## Overview
* [Installation - iOS](#installation---ios)
* [Installation - Android](#installation---android)
* [Usage](#usage)
* [Top Level API](#top-level-api)
* [Screen API](#screen-api)
* [Styling the navigator](#styling-the-navigator)
* [Adding buttons to the navigator](#adding-buttons-to-the-navigator)
## Installation - iOS
* In your project folder run `npm install react-native-navigation --save`
* Add the native files of the dependency [react-native-controllers](https://github.com/wix/react-native-controllers) to your Xcode project:
* In Xcode, in Project Navigator (left pane), right-click on the `Libraries` > `Add files to [project name]`. Add `./node_modules/react-native-controllers/ios/ReactNativeControllers.xcodeproj` ([screenshots](https://facebook.github.io/react-native/docs/linking-libraries-ios.html#step-1))
* In Xcode, in Project Navigator (left pane), click on your project (top) and select the `Build Phases` tab (right pane). In the `Link Binary With Libraries` section add `libReactNativeControllers.a` ([screenshots](https://facebook.github.io/react-native/docs/linking-libraries-ios.html#step-2))
* In Xcode, in Project Navigator (left pane), click on your project (top) and select the `Build Settings` tab (right pane). In the `Header Search Paths` section add `$(SRCROOT)/../node_modules/react-native-controllers/ios`. Make sure on the right to mark this new path `recursive` ([screenshots](https://facebook.github.io/react-native/docs/linking-libraries-ios.html#step-3))
* In Xcode, under your project files, modify `AppDelegate.m` according to this [example](https://github.com/wix/react-native-navigation/blob/master/example/ios/example/AppDelegate.m)
* Make sure you are using react-native version >= 0.19.0
## Installation - Android
Coming soon, not yet supported
## Usage
If you don't like reading, just jump into the fully working [example project](https://github.com/wix/react-native-navigation/tree/master/example).
#### Step 1 - Change the way your app starts
This would normally go in your `index.ios.js`
```js
import { Navigation } from 'react-native-navigation';
// import the components for your root screens (or the packager will not bundle them)
// they all need to be registered with Navigation.registerScreen
import './FirstTabScreen';
import './SecondTabScreen';
// start the app
Navigation.startTabBasedApp({
tabs: [
{
label: 'One',
screen: 'example.FirstTabScreen',
icon: require('../img/one.png'),
selectedIcon: require('../img/one_selected.png'),
title: 'Screen One'
},
{
label: 'Two',
screen: 'example.SecondTabScreen',
icon: require('../img/two.png'),
selectedIcon: require('../img/two_selected.png'),
title: 'Screen Two'
}
]
});
```
#### Step 2 - Slightly modify your screen components
Every screen that you want to be able to place in a tab, push to the navigation stack or present modally needs to follow two basic conventions:
1. Normally your React components extend `React.Component`, in order to get access to the `navigator` instance you need to extend `Screen` instead.
2. You need to register your component since it's displayed as a separate React root. Register a unique ID with `Navigation.registerScreen`.
> Note: Since your screens will potentially be bundled with other packages, your registered name must be **unique**! Follow a namespacing convention like `packageName.ScreenName`.
```js
import { Navigation, Screen } from 'react-native-navigation';
class ExampleScreen extends Screen {
static navigatorStyle = {}; // style the navigator for this screen (optional)
constructor(props) {
super(props);
}
render() {
return (
...
);
}
}
// register all screens with Navigation.registerScreen
Navigation.registerScreen('example.ScreenOne', () => ExampleScreen);
```
## Top Level API
#### `Navigation`
```js
import { Navigation } from 'react-native-navigation';
```
* **registerScreen(screenID, generator)**
Every screen used must be registered with a unique name.
```js
Navigation.registerScreen('example.FirstTabScreen', () => FirstTabScreen);
```
* **startTabBasedApp(params)**
Change your app root into an app based on several tabs (usually 2-5), a very common pattern in iOS (like Facebook app or the iOS Contacts app). Every tab has its own navigation stack with a native nav bar.
```js
Navigation.startTabBasedApp({
tabs: [
{
label: 'One', // tab label as appears under the icon in iOS (optional)
screen: 'example.FirstTabScreen', // unique ID registered with Navigation.registerScreen
icon: require('../img/one.png'), // local image asset for the tab icon unselected state (optional)
selectedIcon: require('../img/one_selected.png'), // local image asset for the tab icon selected state (optional)
title: 'Screen One', // title of the screen as appears in the nav bar (optional)
navigatorStyle: {} // override the navigator style for the tab screen, see "Styling the navigator" below (optional)
},
{
label: 'Two',
screen: 'example.SecondTabScreen',
icon: require('../img/two.png'),
selectedIcon: require('../img/two_selected.png'),
title: 'Screen Two'
}
]
});
```
* **startSingleScreenApp(params)**
Change your app root into an app based on a single screen (like the iOS Calendar or Settings app). The screen will receive its own navigation stack with a native nav bar
```js
Navigation.startSingleScreenApp({
screen: {
screen: 'example.WelcomeScreen', // unique ID registered with Navigation.registerScreen
title: 'Welcome', // title of the screen as appears in the nav bar (optional)
navigatorStyle: {} // override the navigator style for the screen, see "Styling the navigator" below (optional)
}
});
```
* **showModal(params = {})**
Show a screen as a modal.
```js
Navigation.showModal({
screen: "example.ModalScreen", // unique ID registered with Navigation.registerScreen
title: "Modal", // title of the screen as appears in the nav bar (optional)
passProps: {}, // simple serializable object that will pass as props to the modal (optional)
navigatorStyle: {}, // override the navigator style for the screen, see "Styling the navigator" below (optional)
animationType: 'slide-up' // 'none' / 'slide-up' , appear animation for the modal (optional, default 'slide-up')
});
```
* **dismissModal(params = {})**
Dismiss the current modal.
```js
Navigation.dismissModal({
animationType: 'slide-down' // 'none' / 'slide-down' , dismiss animation for the modal (optional, default 'slide-down')
});
```
## Screen API
This API is relevant when in a screen context - it allows a screen to push other screens, pop screens, change its navigator style, etc. Access to this API is available through the `navigator` object. When your screen components extend `Screen`, they have `this.navigator` available and initialized.
* **push(params)**
Push a new screen into this screen's navigation stack.
```js
this.navigator.push({
screen: 'example.ScreenThree', // unique ID registered with Navigation.registerScreen
title: undefined, // navigation bar title of the pushed screen (optional)
passProps: {}, // simple serializable object that will pass as props to the pushed screen (optional)
animated: true, // does the push have transition animation or does it happen immediately (optional)
backButtonTitle: undefined, // override the back button title (optional)
navigatorStyle: {} // override the navigator style for the pushed screen (optional)
});
```
* **pop(params = {})**
Pop the top screen from this screen's navigation stack.
```js
this.navigator.pop({
animated: true // does the pop have transition animation or does it happen immediately (optional)
});
```
* **showModal(params = {})**
Show a screen as a modal.
```js
this.navigator.showModal({
screen: "example.ModalScreen", // unique ID registered with Navigation.registerScreen
title: "Modal", // title of the screen as appears in the nav bar (optional)
passProps: {}, // simple serializable object that will pass as props to the modal (optional)
navigatorStyle: {}, // override the navigator style for the screen, see "Styling the navigator" below (optional)
animationType: 'slide-up' // 'none' / 'slide-up' , appear animation for the modal (optional, default 'slide-up')
});
```
* **dismissModal(params = {})**
Dismiss the current modal.
```js
this.navigator.dismissModal({
animationType: 'slide-down' // 'none' / 'slide-down' , dismiss animation for the modal (optional, default 'slide-down')
});
```
## Styling the navigator
You can style the navigator appearance and behavior by passing a `navigatorStyle` object. This object can be passed when the screen is originally created; can be defined per-screen in the `static navigatorStyle = {};` on `Screen`; and can be overridden when a screen is pushed.
#### Style object format
```js
{
navBarTextColor: '#000000', // change the text color of the title (remembered across pushes)
navBarBackgroundColor: '#f7f7f7', // change the background color of the nav bar (remembered across pushes)
navBarButtonColor: '#007aff', // change the button colors of the nav bar (eg. the back button) (remembered across pushes)
navBarHidden: false, // make the nav bar hidden
navBarHideOnScroll: false, // make the nav bar hidden only after the user starts to scroll
navBarTranslucent: false, // make the nav bar semi-translucent, works best with drawUnderNavBar:true
drawUnderNavBar: false, // draw the screen content under the nav bar, works best with navBarTranslucent:true
drawUnderTabBar: false, // draw the screen content under the tab bar (the tab bar is always translucent)
statusBarBlur: false, // blur the area under the status bar, works best with navBarHidden:true
navBarBlur: false, // blur the entire nav bar, works best with drawUnderNavBar:true
tabBarHidden: false, // make the screen content hide the tab bar (remembered across pushes)
statusBarHideWithNavBar: false // hide the status bar if the nav bar is also hidden, useful for navBarHidden:true
statusBarHidden: false, // make the status bar hidden regardless of nav bar state
statusBarTextColorScheme: 'dark' // text color of status bar, 'dark' / 'light' (remembered across pushes)
}
```
> Note: If you set any styles related to the Status Bar, make sure that in Xcode > project > Info.plist, the property `View controller-based status bar appearance` is set to `YES`.
All supported styles are defined [here](https://github.com/wix/react-native-controllers#styling-navigation). There's also an example project there showcasing all the different styles.
#### Screen-specific style example
Define a screen-specific style by adding `static navigatorStyle = {...};` to the Screen definition.
```js
class StyledScreen extends Screen {
static navigatorStyle = {
drawUnderNavBar: true,
drawUnderTabBar: true,
navBarTranslucent: true
};
constructor(props) {
super(props);
}
render() {
return (
...
);
}
```
## Adding buttons to the navigator
Nav bar buttons can be defined per-screen by adding `static navigatorButtons = {...};` on the Screen definition. Handle onPress events for the buttons by overriding the Screen's `onNavigatorEvent(event)` method.
#### Buttons object format
```js
{
rightButtons: [], // buttons for the right side of the nav bar (optional)
leftButtons: [] // buttons for the left side of the nav bar (optional)
}
```
#### Screen-specific buttons example
```js
class FirstTabScreen extends Screen {
static navigatorButtons = {
rightButtons: [
{
title: 'Edit', // for a textual button, provide the button title (label)
id: 'edit' // id for this button, given in onNavigatorEvent(event) to help understand which button was clicked
},
{
icon: require('../../img/navicon_add.png'), // for icon button, provide the local image asset name
id: 'add' // id for this button, given in onNavigatorEvent(event) to help understand which button was clicked
}
]
};
constructor(props) {
super(props);
}
onNavigatorEvent(event) { // this is the onPress handler for the two buttons together
if (event.id == 'edit') { // this is the same id field from the static navigatorButtons definition
AlertIOS.alert('NavBar', 'Edit button pressed');
}
if (event.id == 'add') {
AlertIOS.alert('NavBar', 'Add button pressed');
}
}
render() {
return (
...
);
}
```