{"id":26222704,"url":"https://github.com/wix/react-native-controllers","last_synced_at":"2025-03-12T17:01:58.339Z","repository":{"id":57336239,"uuid":"45825353","full_name":"wix-incubator/react-native-controllers","owner":"wix-incubator","description":"Native IOS Navigation for React Native (navbar, tabs, drawer)","archived":false,"fork":false,"pushed_at":"2023-03-20T07:32:20.000Z","size":503,"stargazers_count":611,"open_issues_count":1,"forks_count":82,"subscribers_count":340,"default_branch":"master","last_synced_at":"2024-10-29T13:50:02.149Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Objective-C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wix-incubator.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":".github/CONTRIBUTING","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2015-11-09T08:33:57.000Z","updated_at":"2024-09-26T10:50:26.000Z","dependencies_parsed_at":"2024-01-08T01:02:57.112Z","dependency_job_id":"6bf54962-9cb4-4d9b-a9f9-12735bd3813a","html_url":"https://github.com/wix-incubator/react-native-controllers","commit_stats":null,"previous_names":["wix/react-native-controllers"],"tags_count":33,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wix-incubator%2Freact-native-controllers","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wix-incubator%2Freact-native-controllers/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wix-incubator%2Freact-native-controllers/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wix-incubator%2Freact-native-controllers/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wix-incubator","download_url":"https://codeload.github.com/wix-incubator/react-native-controllers/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243258487,"owners_count":20262298,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-03-12T17:01:48.743Z","updated_at":"2025-03-12T17:01:58.307Z","avatar_url":"https://github.com/wix-incubator.png","language":"Objective-C","readme":"# React Native Controllers\n\n\u003e**Important:**\nThis project was deprecated and merged into [react-native-navigation](https://github.com/wix/react-native-navigation). For more info please view [React Native Controllers Evolution](https://github.com/wix/react-native-controllers/issues/87)\n\n---\n\n`react-native-controllers` is a [React Native](https://facebook.github.io/react-native/) npm extension package for iOS which provides a completely native skeleton for iOS apps, allowing you to easily wrap core native UI components without compromising on the native experience. Key benefits:\n\n* Truly native navigation (instead of the JS-based [`Navigator`](https://facebook.github.io/react-native/docs/navigator-comparison.html) or the [deprecated `NavigatorIOS`](https://facebook.github.io/react-native/docs/navigatorios.html))\n* Truly native side menu drawer (instead of the JS-based alternatives available today)\n* Smoother animations, improved performance and look and feel that matches the OS for all iOS versions\n\n*Versions 1.x.x support react-native 0.19.0-0.25.0*\n\n*Versions 2.x.x support react-native 0.25.0+ (due to breaking changes in React Native)*\n\n![Screenshots](http://i.imgur.com/2QyOm9a.png)\n\nWithout `react-native-controllers`, iOS skeleton components such as [`UINavigationController`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UINavigationController_Class/) or a [side menu drawer](https://www.cocoacontrols.com/search?q=drawer) are challenging to implement natively. This forces developers to compromise on user experience to use React Native.\n`react-native-controllers` simplifies this by re-introducing [`UIViewController`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/) into the React Native stack.\n\n\u003e Note: The main purpose of this package is to generate discussion around difficulties we found wrapping native iOS skeleton components with React Native. Look at this as a thought experiment with a proposed solution. If you have alternate ideas please share your feedback!\n\n## Before you start\n\nreact-native-controllers is a low-level package with API that wasn't designed to be cross-platform (since it's optimized for native UX). We have another project called [react-native-navigation](https://github.com/wix/react-native-navigation) which is a syntactic sugar wrapper around react-native-controllers. It provides the *same* native goodness with API that is *much* simpler, cross-platform between iOS and Android, and supports redux!\n\n## Table of contents\n\n* [Why do we need this package?](#why-do-we-need-this-package)\n* [What sacrifices did we make?](#what-sacrifices-did-we-make)\n* [Installation](#installation)\n* [Usage](#usage)\n* [API](#api)\n* [Available view controllers](#available-view-controllers)\n* [Credits](#credits)\n* [Release Notes](CHANGES.md)\n* [License](#license)\n\n## Why do we need this package?\n\nIf you're already convinced you need this package, you can skip straight to [Installation](#installation). If not, brace yourself for a long read.\n\n##### First, what's so great about React Native?\n\nAt Wix, we came to React Native from a codebase of two separate fully native stacks - one for iOS (ObjectiveC) and one for Android (Java). The main benefits React Native brings to such an environment are:\n\n * OTA business logic updates (fix bugs in the JS bundle without going through the app stores)\n * Sharing business logic between iOS, Android and even web counterparts ([demolishing silos](http://techcrunch.com/2015/08/26/facebook-react-native/))\n * Improving development velocity and introducing more people in the organization to app development\n * Maintaining our existing high level of UX since our entire UI is fully native and implemented separately\n\nThe last point is critical and what prevented us from adopting many of the cross-platform mobile technologies that popped up in recent years. One of the premises of React Native is that you can keep using any native UI component and won't be required to make UX compromises.\n\n##### But wait.. the UX compromises did come\n\nAfter working with React Native we noticed that we were encouraged to make UX compromises in several places. Here are two examples:\n\n * Navigator - Originally happy to see [`NavigatorIOS`](https://facebook.github.io/react-native/docs/navigatorios.html#content), we were soon disappointed to see that Facebook [gave up on maintaining it](https://facebook.github.io/react-native/docs/navigator-comparison.html#content) and the recommendation is to use the pure-JS alternative [`Navigator`](https://facebook.github.io/react-native/docs/navigator.html#content). That seemed peculiar since using the native component for such a key element as navigation is obviously the superior UX choice. \u003cbr\u003e There's a limit to how well you can fake it with pure JS and the small details like side swipe for back get lost. Also consider what happens if Apple decides to revamp the design of the navigation bar like they did between iOS 6 and 7. Will we really go to the length of providing two separate experiences to owners of different OS versions with the pure JS implementation?\n * Side menu drawer - The side menu is a popular UI pattern and some of our iOS apps use it. We looked for a React Native wrapper around one of the [many native iOS implementations](https://www.cocoacontrols.com/search?q=drawer). We were surprised to see that we found none, although React Native for iOS has been out at the time for over a year. The React Native component community is [thriving](https://react.parts/native) and there's a native wrapper for everything. There must be some underlying reason why all implementations for such a popular component were pure JS.\n\nIt's interesting to see that both examples revolve around components that are parts of the app skeleton. Why are skeleton components so challenging to implement with native elements?\n\n##### Looking for the underlying challenge\n\nOur first instinct for both of the examples listed above was *\"Hey, great! Let's make these components ourselves! It's a great void to fill for the community.\"*\n\nWe dived into the `NavigatorIOS` [implementation](https://github.com/facebook/react-native/blob/master/React/Views/RCTNavigator.m) but things weren't as simple as we've hoped. The implementation is surprisingly complex and seems to have been done by someone very much proficient with the inner workings of the [UIKit](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIKit_Framework/).\n\nWe've hit similar walls with the side menu. One of the main problems was that side menus are based on [`UIViewController`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/) and not [`UIView`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/). React Native steered our component to return a view.\n\nFurthermore, let's take a look at the standard way React Native hooks into `AppDelegate`:\n\n```objc\nRCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation\n                                                    moduleName:@\"DefaultProject\"\n                                             initialProperties:nil\n                                                 launchOptions:launchOptions];\nself.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];\nUIViewController *rootViewController = [[UIViewController alloc] init];\nrootViewController.view = rootView;\nself.window.rootViewController = rootViewController;\n```\n\nThe entire React Native component hierarchy is contained inside a view and this view is wrapped manually in a view controller. That pretty much summed up our problem. App skeletons in iOS are built from view controllers, but it wasn't obvious to us how those fit in within the React Native world.\n\n##### Giving view controllers a prominent seat at the table\n\nIf our assumption is correct, then giving view controllers a more prominent seat at the table might make skeleton components easier to implement.\n\nThere are many ways to approach this, we've decided to start with a way that won't interfere with the inner-workings of React Native. A good start would be an optional [npm](https://www.npmjs.com/) package that would add this functionality on top.\n\nWith the standard way to use React Native, `RCTRootView` starts at the very top of your app hierarchy (filling the content of `rootViewController`). We wanted to move `RCTRootView` a little lower. The top of the app hierarchy will be filled with natively implemented view controllers. Inside every view controller, the content view could be a separate `RCTRootView`.\n\nThis means that instead of a single `RCTRootView`, our app will have several ones running in parallel. It turns out that this isn't a problem and is [supported out of the box](https://github.com/facebook/react-native/blob/master/React/Base/RCTRootView.h) by the framework. All you need to do is have a single `RCTBridge` that all of them will share. This gives all of these views the same JS execution context, which means they will easily be able to share variables and singletons.\n\nLet's compare the two approaches with a simple two-tab app. [`UITabBarController`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITabBarController_Class/) is a good candidate for a skeleton component since it's based primarily on view controllers:\n\n![Comparison Diagram](http://i.imgur.com/Siyx0JU.png)\n\n##### A few implementation details\n\nThe basis of the proposed approach is that the view controller layer is not implemented as traditional React Native components. Instead, they are implemented natively. We also want to make layouting very flexible so instead of having to write native ObjectiveC code inside Xcode to specify which view controllers you want, we prefer a declarative way.\n\nThe natural choice to define layouts is using XML. This implies that we need to add an additional XML layout file to the project that will be saved right next to the React Native JS bundle. The native package code will then parse this XML file and instantiate the native view controllers when the app is loaded.\n\nThe drawbacks of this external XML is that it's more difficult to update. React Native developers are used to updating everything though the JS bundle. This means the natural place for our XML is to be embedded inside the bundle. This makes for another natural choice for the XML flavor. It should be [JSX](https://facebook.github.io/react/docs/jsx-in-depth.html) to keep things familiar.\n\nIt isn't straightforward to use JSX for layouting things that aren't React components since JSX is [tightly coupled](https://facebook.github.io/react/docs/jsx-in-depth.html#the-transform) with React. Our somewhat hackish solution was to override `React` in the JS modules dedicated for defining view controllers.\n\n##### TL;DR\n\nWe need this package because it allows us to *easily* wrap native components that are part of the app skeleton and rely on `UIViewController` instead of `UIView`. Some examples of these skeleton components are navigation, tabs and side menu drawer. Using this package we are able to use the original native components instead of compromising on pure JS alternatives.\n\n## What sacrifices did we make?\n\nBy using the proposed approach, we made several scarifices that you should be aware of:\n\n* **Predictable state \"leaks\" outside the JS realm** - One of the powerful concepts of React is that components can rely only on `props` and `state` and render themselves accordingly. This means the entire app state can be made predictable and contained within these inputs. Pushing this concept further, using frameworks like [Redux](https://github.com/rackt/redux) you can [time travel](https://www.youtube.com/watch?v=xsSnOQynTHs) to previous states - a supercharged debugging tool.\u003cbr\u003e\u003cbr\u003eThis concept holds as long as the entire app state is located in the JS realm. As you can guess, what we're doing is \"leaking\" crucial app state - like the entire navigation stack - into native components like `UINavigationController`. Since this state is not owned anymore by JS, you will not be able to time travel.\u003cbr\u003e\u003cbr\u003eIn our defense, time travel is a very foreign concept to native mobile development. State is expected to be held by native OS components, that's how the entire world of native mobile development works.\n\n* **Lack of state synchronization between JS and native** - The other side of the same coin is whether we go to the trouble of synchronizing this state between the JS and native realms. This is theoretically possible, see the locking mechanism implemented in [`NavigatorIOS`](https://github.com/facebook/react-native/blob/master/React/Views/RCTNavigator.m). We chose in favor of simplifying the implementation and ignored this problem altogether.\u003cbr\u003e\u003cbr\u003eIn our defense, we're not sure how big of an issue this actually is. The standard native API for navigation changes in [`UINavigationController`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UINavigationController_Class/#//apple_ref/occ/instm/UINavigationController/pushViewController:animated:) is asynchronous due to transition animations. When a controller pushes another, the point in time when the second controller is mounted isn't very well defined. The standard API doesn't provide a completion handler for this event.\u003cbr\u003e\u003cbr\u003eWe believe this decision can hold well for most common straightforward cases of in-app navigation. If we face more complicated cases, adding a completion handler to our components' push/pop JS interface may be an elegant solution.\n\n\n## Installation\n\nYou need an iOS React Native project ([instructions on how to create one](https://facebook.github.io/react-native/docs/getting-started.html#quick-start))\n\n\u003e Important: Make sure you are using react-native version \u003e= 0.25.1\n\n1. Run `npm install react-native-controllers --save` in your project root\n2. In Xcode, in Project Navigator (left pane), right-click on the `Libraries` \u003e `Add files to [project name]` \u003cbr\u003e Add `./node_modules/react-native-controllers/ios/ReactNativeControllers.xcodeproj` ([screenshots](https://facebook.github.io/react-native/docs/linking-libraries-ios.html#step-1))\n3. In Xcode, in Project Navigator (left pane), click on your project (top) and select the `Build Phases` tab (right pane) \u003cbr\u003e In the `Link Binary With Libraries` section add `libReactNativeControllers.a` ([screenshots](https://facebook.github.io/react-native/docs/linking-libraries-ios.html#step-2))\n4. In Xcode, in Project Navigator (left pane), click on your project (top) and select the `Build Settings` tab (right pane) \u003cbr\u003e In the `Header Search Paths` section add `$(SRCROOT)/../node_modules/react-native-controllers/ios` \u003cbr\u003e 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))\n\n## Usage\n\nCheck out the iOS example project under [`./example`](example) to see everything in action. For a detailed explanation of how to modify your project, follow the 3 steps below:\n\n### Step 1 - Update AppDelegate\n\nSince `react-native-controllers` takes over the skeleton of your app, we're first going to change how React Native is invoked in `AppDelegate.m`. In Xcode, change your AppDelegate to look like this:\n\n```objc\n#import \"AppDelegate.h\"\n#import \"RCCManager.h\" // RCC stands for ReaCtControllers\n\n@implementation AppDelegate\n\n- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions\n{\n  NSURL *jsCodeLocation;\n\n  // OPTION 1 - load JS code from development server\n  jsCodeLocation = [NSURL URLWithString:@\"http://localhost:8081/index.ios.bundle?platform=ios\u0026dev=true\"];\n\n  // OPTION 2 - load JS from pre-bundled file on disk\n  // jsCodeLocation = [[NSBundle mainBundle] URLForResource:@\"main\" withExtension:@\"jsbundle\"];\n\n  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];\n\n  // this is your new React Native invocation\n  [[RCCManager sharedInstance] initBridgeWithBundleURL:jsCodeLocation launchOptions:launchOptions];\n\n  return YES;\n}\n\n@end\n```\n\n### Step 2 - Update index.ios.js\n\nWith React Native, every JS file in your project is a module. With `react-native-controllers`, you can have JS modules that deal with `ViewControllers` and you can have JS modules that deal with `Views` (these are the React Native modules that you're used to). It's not recommended to deal with both in the same JS module.\n\nRoughly speaking, `ViewControllers` make the skeleton of your app and `Views` make the actual content of every screen. Since `index.ios.js` is where your skeleton is defined, this module will be dedicated to deal with `ViewControllers`. Normally, this would be the only module that does that and the rest of your JS files will be regular React Native files.\n\nYou can see a complete example of `index.ios.js` [here](example/index.ios.js). If you don't want the full explanation of what's going on in there, just skip to the next step.\n\n##### Making a module deal with ViewControllers instead of Views\n\nTo allow you to use [JSX](https://facebook.github.io/react/docs/jsx-in-depth.html) to define your view controller hierarchy, we're going to have to hijack `React`. Don't worry, this hijack is local to this module only. That's why we don't mix the two types. Your other regular JS modules (that deal with Views) won't have this hijack.\n\nTo perform the hijack, define `React` on the top of the module like this:\n\n```js\nvar Controllers = require('react-native-controllers');\nvar React = Controllers.hijackReact();\n```\n\n\u003e Note: The React instance here is irregular. It can't do many of the things that the original class can, so keep its usage pretty close to our purpose.\n\n##### Defining the view controller hierarchy\n\nWe tried to keep the syntax familiar. Define your view controller skeleton like this:\n\n```jsx\nvar MoviesApp = Controllers.createClass({\n  render: function() {\n    return (\n      \u003cDrawerControllerIOS id=\"drawer\" componentLeft=\"SideMenu\" componentRight=\"SideMenu\"\u003e\n        \u003cTabBarControllerIOS id=\"main\"\u003e\n          \u003cTabBarControllerIOS.Item title=\"Movies\" icon={require('./img/home.png')}\u003e\n            \u003cNavigationControllerIOS title=\"Welcome\" component=\"MovieListScreen\" id=\"movies\" /\u003e\n          \u003c/TabBarControllerIOS.Item\u003e\n          \u003cTabBarControllerIOS.Item title=\"Search\" icon={require('./img/discover.png')}\u003e\n            \u003cViewControllerIOS component=\"SearchScreen\" /\u003e\n          \u003c/TabBarControllerIOS.Item\u003e\n        \u003c/TabBarControllerIOS\u003e\n      \u003c/DrawerControllerIOS\u003e\n    );\n  },\n});\n```\n\n\u003e Note: In this hierarchy you can only use view controllers. You can't use your traditional React Native components. For a full list of all supported view controllers see the rest of this doc.\n\nSome of the view controllers in the hierarchy will have to contain your views. These views are the regular React Native components that you're used to. You will normally define these components in a dedicated JS module - where `React` wasn't hijacked and you can use all of its goodness.\n\nTo hook up a view, use the `component` attribute of the view controller in the JSX and provide the registered name of the component. Every view that you use has to be registered using `AppRegistry.registerComponent()`. For example, the movie list screen content is defined in [`MovieListScreen.js`](example/MovieListScreen.js) and there it's also registered with:\n\n```js\nAppRegistry.registerComponent('MovieListScreen', () =\u003e MovieListScreen);\n```\n\n##### Require all the view components that you referenced\n\nWe have to tell the React Native bundler that we need the components that we just referenced. The easiest way to do this and make sure their JS files are included in our bundle, is to `require` them. You can add this right before you define your view controller hierarchy:\n\n```js\n// require all top level react components you refer to in the layout\nrequire('./SideMenu');\nrequire('./MovieListScreen');\nrequire('./SearchScreen');\n```\n\n##### Register your controller and set it as root\n\nJust like we register regular React Native view modules, we'll need to register the view controller module we've just defined. In the end of `index.ios.js` add the following lines:\n\n```js\nControllerRegistry.registerController('MoviesApp', () =\u003e MoviesApp);\n\n// this line makes the app actually start and initialize\nControllerRegistry.setRootController('MoviesApp');\n```\n\nThe last line is the magic line that bootstraps our entire app. When you set your controller (that you've just defined in JS) as root with `ControllerRegistry.setRootController()`, behind the scenes the native code sets it as `appDelegate.window.rootViewController`. Here you can see that the module that you've just defined is actually a [`UIViewController`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/) and not a [`UIView`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/).\n\n### Step 3 - Implement all top level components\n\nThe view controller hierarchy we've defined in `index.ios.js` references the top level screens of your app. These screens are regular React Native views. They are the exact same components you've been writing up until now.\n\nIn the previous step we've referenced 3 top level components: `SideMenu`, `MovieListScreen`, `SearchScreen`. The best practice is to implement each of them as its own JS module. You can see them implemented here: [`SideMenu.js`](example/SideMenu.js), [`MovieListScreen.js`](example/MovieListScreen.js), [`SearchScreen.js`](example/SearchScreen.js).\n\nAs you can see, these are standard React Native components. They don't require any special changes due to our package. The only requirement is that all of them are registered using React Native's `AppRegistry.registerComponent()`. Only top level components referenced by the view controller hierarchy need to be registered. Register them by adding the following line at the end:\n\n```js\nAppRegistry.registerComponent('MovieListScreen', () =\u003e MovieListScreen);\n```\n\nWhen implementing your components, you may need to interact with one of the view controllers. For example, create a button that pushes a new screen to the navigation controller. You have simple JS API for this purpose available by `require('react-native-controllers')`. This API is documented under the list of [available view controllers](#available-view-controllers).\n\n## API\n\n#### `Controllers`\n\n```js\nvar Controllers = require('react-native-controllers');\n```\n\n * **hijackReact()** - change the React instance in this file so controllers can be defined (see explanation above)\n\n```js\nvar React = Controllers.hijackReact();\nvar {\n  TabBarControllerIOS,\n  NavigationControllerIOS,\n  ViewControllerIOS,\n  DrawerControllerIOS\n} = React;\n```\n\n * **createClass(controllerClass)** - define a new controller using JSX (see explanation above)\n\n```js\nvar MoviesApp = Controllers.createClass({\n  render: function() {\n    return (\n      \u003cDrawerControllerIOS id=\"drawer\" componentLeft=\"SideMenu\" componentRight=\"SideMenu\"\u003e\n      ...\n      \u003c/DrawerControllerIOS\u003e\n    );\n  }\n});\n```\n\n\u003e Note: Your render function can only contain supported controllers, see the full list under [available view controllers](#available-view-controllers).\n\n#### `ControllerRegistry`\n\n```js\nvar Controllers = require('react-native-controllers');\nvar { ControllerRegistry } = Controllers;\n```\n\n * **registerController(controllerId, generator)** - register a unique id for a controller\n\n```js\nControllerRegistry.registerController('MoviesApp', () =\u003e MoviesApp);\n```\n\n * **setRootController(controllerId, animationType = 'none', passProps = {})** - start the app with a root controller\n\n```js\n// example without animation\n// controllerId: a string id previously registered with ControllerRegistry.registerController\nControllerRegistry.setRootController('MoviesApp');\n\n// example with animation, useful for changing your app root during runtime (from a different controller)\n// animationType: 'none', 'slide-down', 'fade'\nControllerRegistry.setRootController('LoginApp', 'slide-down');\n\n// example with props\n// these props will be passed to all top components in the layout hierarchy (eg. all tabs, side menus, etc.)\nControllerRegistry.setRootController('MoviesApp', 'none', { greeting: 'hello world' });\n```\n\n#### `Modal`\n\n```js\nvar Controllers = require('react-native-controllers');\nvar { Modal } = Controllers;\n```\n\n * **showController(controllerId, animationType = 'slide-up', passProps = {})** - display a controller modally\n\n```js\n// example with default slide up animation\n// controllerId: a string id previously registered with ControllerRegistry.registerController\nModal.showController('MoviesApp');\n\n// example without animation\n// animationType: 'none', 'slide-up'\nModal.showController('LoginApp', 'none');\n\n// example with props\nModal.showController('MoviesApp', 'slide-up', { greeting: 'hello world' });\n\n```\n\n * **dismissController(animationType = 'slide-down')** - dismiss the current modal controller\n\n```js\n// example with default slide down animation\nModal.dismissController();\n\n// example without animation\n// animationType: 'none', 'slide-down'\nModal.dismissController('none');\n```\n\n* **dismissAllControllers(animationType = 'slide-down')** - dismiss all displayed modal controllers\n\n```js\n// this will dismiss several displayed controllers at once\nModal.dismissAllControllers();\n```\n\n * **showLightBox(params)** - display a component as a light box\n\n```js\nModal.showLightBox({\n  component: \"LightBoxScreen\", // the unique ID registered with AppRegistry.registerComponent (required)\n  passProps: {}, // simple serializable object that will pass as props to lightbox component (optional)\n  style: {\n    backgroundBlur: \"dark\", // 'dark' / 'light' / 'xlight' / 'none' - the type of blur on the background\n    backgroundColor: \"#ff000080\" // tint color for the background, you can specify alpha here (optional)\n  }\n});\n```\n\n * **dismissLightBox()** - dismiss the current light box\n\n```js\nModal.dismissLightBox();\n```\n\n#### Components\n\n##### `NavigationToolBarIOS`\n\nHelper component to assist with adding custom views to the bottom of the navigation bar. You can see this UI pattern in the native iOS Calendar app (week days in the day view) and the native iOS Health app (segmented control in the dashboard tab).\n\nThis pattern can be implemented by adding a React component in your **screen component's content** (not really on the nav bar) and making it stick to top (using absolute position). The illusion that this component is part of the nav bar is achieved by wrapping it with `NavigationToolBarIOS ` which provides a background with the same translucent effect the nav bar has.\n\nYou can see a working example of all this in the example project.\n\n```js\nvar Controllers = require('react-native-controllers');\nvar { NavigationToolBarIOS } = Controllers;\n```\n\n###### Example (holding SegmentedControlIOS)\n\n```js\n\u003cNavigationToolBarIOS key='segmented' style={{\n  top: 44,\n  width: width,\n  height: 64,\n  position: 'absolute'\n}}\u003e\n  \u003cSegmentedControlIOS\n    values={['One', 'Two', 'Three']}\n    selectedIndex={this.state.segmentIndexSelected}\n    style={styles.segmentedControl}\n    onChange={(event) =\u003e {\n      this.setState({segmentIndexSelected : event.nativeEvent.selectedSegmentIndex});\n    }}\n  /\u003e\n  \u003cView style={styles.lineBorder} /\u003e\n\u003c/NavigationToolBarIOS\u003e\n```\n\n\u003eNote: In order to position this component immediately below the navigation bar, we use `{position: 'absolute'}`. In addition, to make sure z-order is correct and this compoenent is rendered over the rest of the content, add it as the last component in your hierarchy (inside your screen component render method).\n\n###### Props\n\n```jsx\n\u003cNavigationToolBarIOS translucent={true} /\u003e\n```\n\nAttribute | Description\n-------- | -----------\ntranslucent | Boolean, whether the background has the same translucent effect as a nav bar with the `NavBarTranslucent` style. Default `true`.\n\n\n## Available View Controllers\n\nThe package contains implementations for the following view controllers that you can use in your app skeleton:\n\n * [NavigationControllerIOS](#navigationcontrollerios) - Native navigator wrapper around [`UINavigationController`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UINavigationController_Class/)\n * [DrawerControllerIOS](#drawercontrollerios) - Native side menu drawer wrapper around [`MMDrawerController`](https://github.com/mutualmobile/MMDrawerController)\n * [TabBarControllerIOS](#tabbarcontrollerios) - Native tabs wrapper around [`UITabBarController`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITabBarController_Class/)\n * [ViewControllerIOS](#viewcontrollerios) - Generic empty view controller wrapper around [`UIViewController`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/)\n\nThese wrappers are very simple. You can also add your own if you find missing React Native components that are based on `UIViewController` instead of `UIView`.\n\n#### `NavigationControllerIOS`\n\nNative navigator wrapper around [`UINavigationController`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UINavigationController_Class/). This view controller is a replacement for React Native's [`NavigatorIOS`](https://facebook.github.io/react-native/docs/navigatorios.html#content) that is no longer maintained by Facebook.\n\n##### Props\n\n```jsx\n\u003cNavigationControllerIOS title=\"Welcome\" component=\"MovieListScreen\" id=\"movies\" /\u003e\n```\n\nAttribute | Description\n-------- | -----------\ntitle | Title displayed on the navigation bar on the root view controller (initial route)\ntitleImage | Image to be displayed in the navigation bar instead of the title\ncomponent | [Registered name](https://github.com/wix/react-native-controllers#step-3---implement-all-top-level-components) of the component that provides the view for the root view controller (initial route)\nid | Unique ID used to reference this view controller in future API calls\npassProps | Simple serializable object that will pass as props to the pushed component\nstyle | Style the navigation bar, see [Styling Navigation](#styling-navigation) below for all available styles\nleftButtons | Array of left buttons to add in the nav bar, see `setLeftButtons` below for array format\nrightButtons | Array of right buttons to add in the nav bar, see `setRightButtons` below for array format\n\n##### Methods\n\nGet the instance with `Controllers.NavigationControllerIOS(id)`\n\n```js\nvar Controllers = require('react-native-controllers');\nvar navigationController = Controllers.NavigationControllerIOS(\"movies\");\n```\n\n * **push(params)** - push a new screen\n\n```js\nrequire('./PushedScreen');\nnavigationController.push({\n  title: \"New Screen\", // nav bar title of the pushed screen (optional)\n  titleImage: require('../img/title_image.png'), //nav bar title image of the pushed screen (optional)\n  component: \"PushedScreen\", // the unique ID registered with AppRegistry.registerComponent (required)\n  passProps: {}, // simple serializable object that will pass as props to the pushed component (optional)\n  style: {}, // style the navigation bar for the pushed screen (optional, see \"Styling Navigation\" below)\n  animated: true, // does the push have a transition animation (optional, default true)\n  backButtonTitle: \"Back\", // override the nav bar back button title for the pushed screen (optional)\n  backButtonHidden: true, // hide the nav bar back button for the pushed screen altogether (optional)\n  leftButtons: [{ // buttons in the nav bar of the pushed screen (optional)\n    title: \"Button Title\", // optional, title for a textual button\n    icon: require('./img/navicon_camera.png'), // optional, image for an icon button\n    onPress: function() {\n      // on press event handler\n    },\n    testID: \"e2e_is_awesome\", // optional, used to locate this view in end-to-end tests\n    disabled: true, // optional, disables the button (shown as faded without interaction)\n    disableIconTint: true, // optional, by default the image colors are overridden and tinted to navBarButtonColor, set to true to keep the original image colors\n  }],\n  rightButtons: [] // similar format to leftButtons (optional)\n});\n```\n\n\u003e Important Note: Every pushed component should be registered with `AppRegistry.registerComponent()` like the top level component in a tradition React Native app. This is because every pushed component is actually a new React root under the hood.\n\n * **pop()** - pop the current screen\n\n```js\nnavigationController.pop({\n  animated: true // does the pop have a transition animation (optional, default true)\n);\n```\n\n * **popToRoot()** - pop all the screens from the navigation stack until we get to the root\n\n```js\nnavigationController.popToRoot({\n  animated: true // does the pop have a transition animation (optional, default true)\n);\n```\n\n* **resetTo()** - replace the navigation stack root, all screens in the existing stack will be discarded\n\n```js\nnavigationController.resetTo({\n  title: \"New Screen\", // nav bar title of the new screen (optional)\n  component: \"PushedScreen\", // the unique ID registered with AppRegistry.registerComponent (required)\n  passProps: {}, // simple serializable object that will pass as props to the new component (optional)\n  style: {}, // style the navigation bar for the new screen (optional, see \"Styling Navigation\" below)\n  animated: true, // does the reset have a transition animation (optional, default true)\n  leftButtons: [{ // buttons in the nav bar of the new screen (optional)\n    title: \"Button Title\", // optional, title for a textual button\n    icon: require('./img/navicon_camera.png'), // optional, image for an icon button\n    onPress: function() {\n      // on press event handler\n    },\n    testID: \"e2e_is_awesome\", // optional, used to locate this view in end-to-end tests\n    disabled: true, // optional, disables the button (shown as faded without interaction)\n    disableIconTint: true, // optional, by default the image colors are overridden and tinted to navBarButtonColor, set to true to keep the original image colors\n  }],\n  rightButtons: [] // similar format to leftButtons (optional)\n);\n```\n\n * **setLeftButtons(buttons, animated = false)** - set the left buttons of the navigation bar\n * **setRightButtons(buttons, animated = false)** - set the right buttons of the navigation bar\n\n```js\nnavigationController.setRightButtons([\n  {\n    title: \"Button Title\", // title for a textual button\n    onPress: function() {\n      // on press event handler\n    },\n    testID: \"e2e_is_awesome\", // optional, used to locate this view in end-to-end tests\n    disabled: true, // optional, disables the button (shown as faded without interaction)\n    disableIconTint: true, // optional, by default the image colors are overridden and tinted to navBarButtonColor, set to true to keep the original image colors\n  },\n  {\n    icon: require('./img/navicon_camera.png'), // image for an icon button\n    onPress: function() {\n      // on press event handler\n    },\n    testID: \"e2e_is_awesome\", // optional, used to locate this view in end-to-end tests\n    disabled: true, // optional, disables the button (shown as faded without interaction)\n    disableIconTint: true, // optional, by default the image colors are overridden and tinted to navBarButtonColor, set to true to keep the original image colors\n  }\n  ]);\n```\n\n* **setTitle(params)** - change the title in the navigation bar during runtime\n\n```js\nnavigationController.setTitle({\n  title: \"New Title\"\n});\n```\n\n* **toggleNavBar(animated = true)** - toggle the navigation bar\n\n```js\nnavigationController.toggleNavBar();\n```\n\n##### Styling Navigation\n\nYou can apply styling to the navigation bar appearance and behavior by setting the `style` property when defining your `NavigationControllerIOS` or by passing the `style` object when pushing a new screen.\n\nPlease note that some styles (usually color related) are remembered for future pushed screens. For example, if you change the navigation bar color, all future pushed screens will keep this style and have the same changed colors. If you wish to have different colors in a pushed screen, simply override the style by passing the `style` object when calling `push()`. Every style that is remembered across pushes is documented as such below.\n\nAll styles are optional, this is the format of the style object:\n\n```js\n{\n  navBarTextColor: '#000000', // change the text color of the title (remembered across pushes)\n  navBarBackgroundColor: '#f7f7f7', // change the background color of the nav bar (remembered across pushes)\n  navBarButtonColor: '#007aff', // change the button colors of the nav bar (eg. the back button) (remembered across pushes)\n  navBarHidden: false, // make the nav bar hidden\n  navBarHideOnScroll: false, // make the nav bar hidden only after the user starts to scroll\n  navBarTranslucent: false, // make the nav bar semi-translucent, works best with drawUnderNavBar:true\n  navBarTransparent: false, // make the nav bar transparent, works best with drawUnderNavBar:true\n  navBarNoBorder: false, // hide the navigation bar bottom border (hair line)\n  drawUnderNavBar: false, // draw the screen content under the nav bar, works best with navBarTranslucent:true\n  drawUnderTabBar: false, // draw the screen content under the tab bar (the tab bar is always translucent)\n  statusBarBlur: false, // blur the area under the status bar, works best with navBarHidden:true\n  navBarBlur: false, // blur the entire nav bar, works best with drawUnderNavBar:true\n  tabBarHidden: false, // make the screen content hide the tab bar (remembered across pushes)\n  statusBarHideWithNavBar: false // hide the status bar if the nav bar is also hidden, useful for navBarHidden:true\n  statusBarHidden: false, // make the status bar hidden regardless of nav bar state\n  statusBarTextColorScheme: 'dark' // text color of status bar, 'dark' / 'light' (remembered across pushes)\n}\n```\n\n\u003e Note: If you set any styles related to the Status Bar, make sure that in Xcode \u003e project \u003e Info.plist, the property `View controller-based status bar appearance` is set to `YES`.\n\nSee all the styles in action by running the [example](example) project in Xcode (under the \"Favorites\" tab).\n\n##### Examples\n\n[`FavoritesScreen.js`](example/FavoritesScreen.js), [`PushedScreen.js`](example/PushedScreen.js)\n\n#### `DrawerControllerIOS`\n\nNative side menu drawer wrapper around [`MMDrawerController`](https://github.com/mutualmobile/MMDrawerController). This view controller lets you add a configurable side menu to your app (either on the left, right or both). Unlike most side menu implementations available for React Native, this side menu isn't implemented in JS and is completely native.\n\n##### Props\n\n```jsx\n\u003cDrawerControllerIOS id=\"drawer\" componentLeft=\"SideMenu\" componentRight=\"SideMenu\"\u003e\n  // center view controller here (the body of the app)\n\u003c/DrawerControllerIOS\u003e\n```\n\nAttribute | Description\n-------- | -----------\ncomponentLeft | [Registered name](https://github.com/wix/react-native-controllers#step-3---implement-all-top-level-components) of the component that provides the view for the left side menu\ncomponentRight | [Registered name](https://github.com/wix/react-native-controllers#step-3---implement-all-top-level-components) of the component that provides the view for the right side menu\nid | Unique ID used to reference this view controller in future API calls\npassPropsLeft | Simple serializable object that will pass as props to the left component\npassPropsRight | Simple serializable object that will pass as props to the right component\ndisableOpenGesture | Disable the open gesture\ntype | MMDrawer / TheSideBar (default is MMDrawer)\nanimationType | if type=MMDrawer animationTypes=door/parallax/slide/slide-and-scale. If type=TheSideBar animationType=airbnb/facebook/luvocracy/wunder-list (**Default** is type=MMDrawer animationType=slide)\n\n\n##### Methods\n\nGet the instance with `Controllers.DrawerControllerIOS(id)`\n\n```js\nvar Controllers = require('react-native-controllers');\nvar drawerController = Controllers.DrawerControllerIOS(\"drawer\");\n```\n\n * **open(params)** - open the side menu\n\n```js\ndrawerController.open({\n  side: \"right\",\n  animated: true\n});\n```\n\n * **close(params)** - close the side menu\n\n```js\ndrawerController.close({\n  side: \"right\",\n  animated: true\n});\n```\n\n * **toggle(params)** - toggle the side menu (open if close, close if open)\n\n```js\ndrawerController.toggle({\n  side: \"left\",\n  animated: true\n});\n```\n\n * **setStyle(params)** - set the side menu animation type\n\n```js\ndrawerController.setStyle({\n  animationType: \"slide\" // slide, slideAndScale, parallax, door\n});\n```\n##### Styling Drawer\n\nYou can apply styling to the Drawer appearance and behavior by setting the `style` property when defining your `DrawerControllerIOS `.\n\nAll styles are optional, this is the format of the style object:\n\n```js\n{\n  contentOverlayColor: '#162D3D55', // change the text color of the title (support colors with alpha - last 2 digits)\n  backgroundImage: 'icon={require('./img/background.png')}', // change the background image. Will be seen when Drawer animationType are slide-and-scale or airbnb/luvacracy\n  leftDrawerWidth: '60' // change the left drawer width. Precentage value, 60% of screen width (default is 80%)\n  rightDrawerWidth: '70' // change the left drawer width. Precentage value, 70% of screen width (default is 80%)\n}\n```\n\nSee all the styles in action by running the [example](example) project in Xcode (under the \"Movies\" tab, side menu section, try to press \"More...\" button in order to show more cool drawer options).\n\n\n\n##### Examples\n\n[`MovieListScreen.js`](example/MovieListScreen.js)\n\n#### `TabBarControllerIOS`\n\nNative tabs wrapper around [`UITabBarController`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITabBarController_Class/). This view controller lets display native tabs in your app, much like React Native's [`TabBarIOS`](https://facebook.github.io/react-native/docs/tabbarios.html#content).\n\n##### Props\n\n```jsx\n\u003cTabBarControllerIOS id=\"main\"\u003e\n  \u003cTabBarControllerIOS.Item title=\"Movies\" icon={require('./img/home.png')}\u003e\n    // view controller here (the body of the tab)\n  \u003c/TabBarControllerIOS.Item\u003e\n  \u003cTabBarControllerIOS.Item title=\"Search\" icon={require('./img/discover.png')}\u003e\n    // view controller here (the body of the tab)\n  \u003c/TabBarControllerIOS.Item\u003e\n\u003c/TabBarControllerIOS\u003e\n```\n\nAttribute | Description\n-------- | -----------\nid | Unique ID used to reference this view controller in future API calls\nstyle | Style the tab bar, see [Styling Tab Bar](#styling-tab-bar) below for all available styles\n\n\nItem Attribute | Description\n-------- | -----------\ntitle | Title displayed on the tab label\nicon | Local asset image for the tab icon (unselected state), use `require` like with a [local image](https://facebook.github.io/react-native/docs/image.html)\nselectedIcon | Local asset image for the tab icon (selected state), use `require` like with a [local image](https://facebook.github.io/react-native/docs/image.html)\nbadge | Badge displayed on tab icon. To keep item without badge set `none` or leave without `badge` property\n\n\u003e Note: For best results on iOS, supply icon images that are 50x50 pixels for retina screen, make sure you add the `@2x` suffix for the filename on disk (eg. `home@2x.png`)\n\n##### Methods\n\nGet the instance with `Controllers.TabBarControllerIOS(id)`\n\n```js\nvar Controllers = require('react-native-controllers');\nvar tabController = Controllers.TabBarControllerIOS(\"main\");\n```\n\n * **switchTo(params)** - switch to one of the tabs\n\n```js\ntabController.switchTo({\n  tabIndex: 0, // if you want to specify the tab by its index (option A)\n  contentId: \"movies_nav\", // if instead of index you want to specify by the contained controller id (option B)\n  contentType: \"NavigationControllerIOS\" // the type of the contained controller (option B)\n});\n```\n\n * **setHidden(params)** - manually hide/show the tab bar\n\n```js\ntabController.setHidden({\n  hidden: true, // the new state of the tab bar\n  animated: true\n});\n```\n\n * **setBadge(params)** - change the badge value on a tab\n\n```js\ntabController.setBadge({\n  tabIndex: 0, // if you want to specify the tab by its index (option A)\n  contentId: \"movies_nav\", // if instead of index you want to specify by the contained controller id (option B)\n  contentType: \"NavigationControllerIOS\", // the type of the contained controller (option B)\n  badge: \"HOT\" // badge value, null to remove badge\n});\n```\n\n##### Styling Tab Bar\n\nYou can apply styling to the tab bar appearance by setting the `style` property when defining your `TabBarControllerIOS`.\n\nAll styles are optional, this is the format of the style object:\n\n```js\n{\n  tabBarButtonColor: '#ffff00', // change the color of the tab icons and text (also unselected)\n  tabBarSelectedButtonColor: '#ff9900', // change the color of the selected tab icon and text (only selected)\n  tabBarBackgroundColor: '#551A8B' // change the background color of the tab bar\n}\n```\n\n#### `ViewControllerIOS`\n\nGeneric empty view controller wrapper around [`UIViewController`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/). This view controller is useful when you need to specify a view controller but you don't want anything special except a holder for your view. For example, a tab body without a navigation controller.\n\n##### Props\n\n```jsx\n\u003cViewControllerIOS component=\"SearchScreen\" /\u003e\n```\n\nAttribute | Description\n-------- | -----------\ncomponent | [Registered name](https://github.com/wix/react-native-controllers#step-3---implement-all-top-level-components) of the component that provides the view for this view controller\n\n##### Methods\n\nCurrently not implemented\n\n## Credits\n\n* [React Native](https://github.com/facebook/react-native) framework by Facebook\n* [MMDrawerController](https://github.com/mutualmobile/MMDrawerController) component by mutualmobile\n\n## License\n\nThe MIT License.\n\nSee [LICENSE](LICENSE)\n","funding_links":[],"categories":["组件","Libraries","Components"],"sub_categories":["UI","Navigation"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwix%2Freact-native-controllers","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwix%2Freact-native-controllers","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwix%2Freact-native-controllers/lists"}