diff --git a/Libraries/Components/Navigation/NavigatorIOS.android.js b/Libraries/Components/Navigation/NavigatorIOS.android.js deleted file mode 100644 index 260d55992..000000000 --- a/Libraries/Components/Navigation/NavigatorIOS.android.js +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - */ - -'use strict'; - -module.exports = require('UnimplementedView'); diff --git a/Libraries/Components/Navigation/NavigatorIOS.ios.js b/Libraries/Components/Navigation/NavigatorIOS.ios.js deleted file mode 100644 index 539b41d4b..000000000 --- a/Libraries/Components/Navigation/NavigatorIOS.ios.js +++ /dev/null @@ -1,932 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - * @flow - */ - -'use strict'; - -const EventEmitter = require('EventEmitter'); -const Image = require('Image'); -const RCTNavigatorManager = require('NativeModules').NavigatorManager; -const React = require('React'); -const PropTypes = require('prop-types'); -const ReactNative = require('ReactNative'); -const StaticContainer = require('StaticContainer.react'); -const StyleSheet = require('StyleSheet'); -const TVEventHandler = require('TVEventHandler'); -const View = require('View'); -const DeprecatedViewPropTypes = require('DeprecatedViewPropTypes'); - -const createReactClass = require('create-react-class'); -const invariant = require('fbjs/lib/invariant'); -const requireNativeComponent = require('requireNativeComponent'); - -/* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error - * found when Flow v0.54 was deployed. To see the error delete this comment and - * run Flow. */ -const keyMirror = require('fbjs/lib/keyMirror'); - -const TRANSITIONER_REF = 'transitionerRef'; - -let __uid = 0; -function getuid() { - return __uid++; -} - -class NavigatorTransitionerIOS extends React.Component<$FlowFixMeProps> { - requestSchedulingNavigation(cb) { - RCTNavigatorManager.requestSchedulingJavaScriptNavigation( - ReactNative.findNodeHandle(this), - cb, - ); - } - - render() { - return ; - } -} - -const SystemIconLabels = { - done: true, - cancel: true, - edit: true, - save: true, - add: true, - compose: true, - reply: true, - action: true, - organize: true, - bookmarks: true, - search: true, - refresh: true, - stop: true, - camera: true, - trash: true, - play: true, - pause: true, - rewind: true, - 'fast-forward': true, - undo: true, - redo: true, - 'page-curl': true, -}; -const SystemIcons = keyMirror(SystemIconLabels); - -type SystemButtonType = $Enum; - -type Route = { - component: Function, - title: string, - titleImage?: Object, - passProps?: Object, - backButtonTitle?: string, - backButtonIcon?: Object, - leftButtonTitle?: string, - leftButtonIcon?: Object, - leftButtonSystemIcon?: SystemButtonType, - onLeftButtonPress?: Function, - rightButtonTitle?: string, - rightButtonIcon?: Object, - rightButtonSystemIcon?: SystemButtonType, - onRightButtonPress?: Function, - wrapperStyle?: any, -}; - -type State = { - idStack: Array, - routeStack: Array, - requestedTopOfStack: number, - observedTopOfStack: number, - progress: number, - fromIndex: number, - toIndex: number, - makingNavigatorRequest: boolean, - updatingAllIndicesAtOrBeyond: ?number, -}; - -type Event = Object; - -/** - * Think of `` as simply a component that renders an - * `RCTNavigator`, and moves the `RCTNavigator`'s `requestedTopOfStack` pointer - * forward and backward. The `RCTNavigator` interprets changes in - * `requestedTopOfStack` to be pushes and pops of children that are rendered. - * `` always ensures that whenever the `requestedTopOfStack` - * pointer is moved, that we've also rendered enough children so that the - * `RCTNavigator` can carry out the push/pop with those children. - * `` also removes children that will no longer be needed - * (after the pop of a child has been fully completed/animated out). - */ - -/** - * `NavigatorIOS` is a wrapper around - * [`UINavigationController`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UINavigationController_Class/), - * enabling you to implement a navigation stack. It works exactly the same as it - * would on a native app using `UINavigationController`, providing the same - * animations and behavior from UIKit. - * - * As the name implies, it is only available on iOS. Take a look at - * [`React Navigation`](https://reactnavigation.org/) for a cross-platform - * solution in JavaScript, or check out either of these components for native - * solutions: [native-navigation](http://airbnb.io/native-navigation/), - * [react-native-navigation](https://github.com/wix/react-native-navigation). - * - * To set up the navigator, provide the `initialRoute` prop with a route - * object. A route object is used to describe each scene that your app - * navigates to. `initialRoute` represents the first route in your navigator. - * - * ``` - * import PropTypes from 'prop-types'; - * import React, { Component } from 'react'; - * import { NavigatorIOS, Text } from 'react-native'; - * - * export default class NavigatorIOSApp extends Component { - * render() { - * return ( - * - * ); - * } - * } - * - * class MyScene extends Component { - * static propTypes = { - * title: PropTypes.string.isRequired, - * navigator: PropTypes.object.isRequired, - * } - * - * _onForward = () => { - * this.props.navigator.push({ - * title: 'Scene ' + nextIndex, - * }); - * } - * - * render() { - * return ( - * - * Current Scene: { this.props.title } - * - * Tap me to load the next scene - * - * - * ) - * } - * } - * ``` - * - * In this code, the navigator renders the component specified in initialRoute, - * which in this case is `MyScene`. This component will receive a `route` prop - * and a `navigator` prop representing the navigator. The navigator's navigation - * bar will render the title for the current scene, "My Initial Scene". - * - * You can optionally pass in a `passProps` property to your `initialRoute`. - * `NavigatorIOS` passes this in as props to the rendered component: - * - * ``` - * initialRoute={{ - * component: MyScene, - * title: 'My Initial Scene', - * passProps: { myProp: 'foo' } - * }} - * ``` - * - * You can then access the props passed in via `{this.props.myProp}`. - * - * #### Handling Navigation - * - * To trigger navigation functionality such as pushing or popping a view, you - * have access to a `navigator` object. The object is passed in as a prop to any - * component that is rendered by `NavigatorIOS`. You can then call the - * relevant methods to perform the navigation action you need: - * - * ``` - * class MyView extends Component { - * _handleBackPress() { - * this.props.navigator.pop(); - * } - * - * _handleNextPress(nextRoute) { - * this.props.navigator.push(nextRoute); - * } - * - * render() { - * const nextRoute = { - * component: MyView, - * title: 'Bar That', - * passProps: { myProp: 'bar' } - * }; - * return( - * this._handleNextPress(nextRoute)}> - * - * See you on the other nav {this.props.myProp}! - * - * - * ); - * } - * } - * ``` - * - * You can also trigger navigator functionality from the `NavigatorIOS` - * component: - * - * ``` - * class NavvyIOS extends Component { - * _handleNavigationRequest() { - * this.refs.nav.push({ - * component: MyView, - * title: 'Genius', - * passProps: { myProp: 'genius' }, - * }); - * } - * - * render() { - * return ( - * this._handleNavigationRequest(), - * }} - * style={{flex: 1}} - * /> - * ); - * } - * } - * ``` - * - * The code above adds a `_handleNavigationRequest` private method that is - * invoked from the `NavigatorIOS` component when the right navigation bar item - * is pressed. To get access to the navigator functionality, a reference to it - * is saved in the `ref` prop and later referenced to push a new scene into the - * navigation stack. - * - * #### Navigation Bar Configuration - * - * Props passed to `NavigatorIOS` will set the default configuration - * for the navigation bar. Props passed as properties to a route object will set - * the configuration for that route's navigation bar, overriding any props - * passed to the `NavigatorIOS` component. - * - * ``` - * _handleNavigationRequest() { - * this.refs.nav.push({ - * //... - * passProps: { myProp: 'genius' }, - * barTintColor: '#996699', - * }); - * } - * - * render() { - * return ( - * - * ); - * } - * ``` - * - * In the example above the navigation bar color is changed when the new route - * is pushed. - * - */ -const NavigatorIOS = createReactClass({ - displayName: 'NavigatorIOS', - - propTypes: { - /** - * NavigatorIOS uses `route` objects to identify child views, their props, - * and navigation bar configuration. Navigation operations such as push - * operations expect routes to look like this the `initialRoute`. - */ - initialRoute: PropTypes.shape({ - /** - * The React Class to render for this route - */ - component: PropTypes.func.isRequired, - - /** - * The title displayed in the navigation bar and the back button for this - * route. - */ - title: PropTypes.string.isRequired, - - /** - * If set, a title image will appear instead of the text title. - */ - titleImage: Image.propTypes.source, - - /** - * Use this to specify additional props to pass to the rendered - * component. `NavigatorIOS` will automatically pass in `route` and - * `navigator` props to the component. - */ - passProps: PropTypes.object, - - /** - * If set, the left navigation button image will be displayed using this - * source. Note that this doesn't apply to the header of the current - * view, but to those views that are subsequently pushed. - */ - backButtonIcon: Image.propTypes.source, - - /** - * If set, the left navigation button text will be set to this. Note that - * this doesn't apply to the left button of the current view, but to - * those views that are subsequently pushed - */ - backButtonTitle: PropTypes.string, - - /** - * If set, the left navigation button image will be displayed using - * this source. - */ - leftButtonIcon: Image.propTypes.source, - - /** - * If set, the left navigation button will display this text. - */ - leftButtonTitle: PropTypes.string, - - /** - * If set, the left header button will appear with this system icon - * - * Supported icons are `done`, `cancel`, `edit`, `save`, `add`, - * `compose`, `reply`, `action`, `organize`, `bookmarks`, `search`, - * `refresh`, `stop`, `camera`, `trash`, `play`, `pause`, `rewind`, - * `fast-forward`, `undo`, `redo`, and `page-curl` - */ - leftButtonSystemIcon: PropTypes.oneOf(Object.keys(SystemIcons)), - - /** - * This function will be invoked when the left navigation bar item is - * pressed. - */ - onLeftButtonPress: PropTypes.func, - - /** - * If set, the right navigation button image will be displayed using - * this source. - */ - rightButtonIcon: Image.propTypes.source, - - /** - * If set, the right navigation button will display this text. - */ - rightButtonTitle: PropTypes.string, - - /** - * If set, the right header button will appear with this system icon - * - * See leftButtonSystemIcon for supported icons - */ - rightButtonSystemIcon: PropTypes.oneOf(Object.keys(SystemIcons)), - - /** - * This function will be invoked when the right navigation bar item is - * pressed. - */ - onRightButtonPress: PropTypes.func, - - /** - * Styles for the navigation item containing the component. - */ - wrapperStyle: DeprecatedViewPropTypes.style, - - /** - * Boolean value that indicates whether the navigation bar is hidden. - */ - navigationBarHidden: PropTypes.bool, - - /** - * Boolean value that indicates whether to hide the 1px hairline - * shadow. - */ - shadowHidden: PropTypes.bool, - - /** - * The color used for the buttons in the navigation bar. - */ - tintColor: PropTypes.string, - - /** - * The background color of the navigation bar. - */ - barTintColor: PropTypes.string, - - /** - * The style of the navigation bar. Supported values are 'default', 'black'. - * Use 'black' instead of setting `barTintColor` to black. This produces - * a navigation bar with the native iOS style with higher translucency. - */ - barStyle: PropTypes.oneOf(['default', 'black']), - - /** - * The text color of the navigation bar title. - */ - titleTextColor: PropTypes.string, - - /** - * Boolean value that indicates whether the navigation bar is - * translucent. - */ - translucent: PropTypes.bool, - }).isRequired, - - /** - * Boolean value that indicates whether the navigation bar is hidden - * by default. - */ - navigationBarHidden: PropTypes.bool, - - /** - * Boolean value that indicates whether to hide the 1px hairline shadow - * by default. - */ - shadowHidden: PropTypes.bool, - - /** - * The default wrapper style for components in the navigator. - * A common use case is to set the `backgroundColor` for every scene. - */ - itemWrapperStyle: DeprecatedViewPropTypes.style, - - /** - * The default color used for the buttons in the navigation bar. - */ - tintColor: PropTypes.string, - - /** - * The default background color of the navigation bar. - */ - barTintColor: PropTypes.string, - - /** - * The style of the navigation bar. Supported values are 'default', 'black'. - * Use 'black' instead of setting `barTintColor` to black. This produces - * a navigation bar with the native iOS style with higher translucency. - */ - barStyle: PropTypes.oneOf(['default', 'black']), - - /** - * The default text color of the navigation bar title. - */ - titleTextColor: PropTypes.string, - - /** - * Boolean value that indicates whether the navigation bar is - * translucent by default - */ - translucent: PropTypes.bool, - - /** - * Boolean value that indicates whether the interactive pop gesture is - * enabled. This is useful for enabling/disabling the back swipe navigation - * gesture. - * - * If this prop is not provided, the default behavior is for the back swipe - * gesture to be enabled when the navigation bar is shown and disabled when - * the navigation bar is hidden. Once you've provided the - * `interactivePopGestureEnabled` prop, you can never restore the default - * behavior. - */ - interactivePopGestureEnabled: PropTypes.bool, - }, - - navigator: (undefined: ?Object), - - UNSAFE_componentWillMount: function() { - // Precompute a pack of callbacks that's frequently generated and passed to - // instances. - this.navigator = { - push: this.push, - pop: this.pop, - popN: this.popN, - replace: this.replace, - replaceAtIndex: this.replaceAtIndex, - replacePrevious: this.replacePrevious, - replacePreviousAndPop: this.replacePreviousAndPop, - resetTo: this.resetTo, - popToRoute: this.popToRoute, - popToTop: this.popToTop, - }; - }, - - componentDidMount: function() { - this._enableTVEventHandler(); - }, - - componentWillUnmount: function() { - this._disableTVEventHandler(); - }, - - getDefaultProps: function(): Object { - return { - translucent: true, - }; - }, - - getInitialState: function(): State { - return { - idStack: [getuid()], - routeStack: [this.props.initialRoute], - // The navigation index that we wish to push/pop to. - requestedTopOfStack: 0, - // The last index that native has sent confirmation of completed push/pop - // for. At this point, we can discard any views that are beyond the - // `requestedTopOfStack`. A value of `null` means we have not received - // any confirmation, ever. We may receive an `observedTopOfStack` without - // ever requesting it - native can instigate pops of its own with the - // backswipe gesture. - observedTopOfStack: 0, - progress: 1, - fromIndex: 0, - toIndex: 0, - // Whether or not we are making a navigator request to push/pop. (Used - // for performance optimization). - makingNavigatorRequest: false, - // Whether or not we are updating children of navigator and if so (not - // `null`) which index marks the beginning of all updates. Used for - // performance optimization. - updatingAllIndicesAtOrBeyond: 0, - }; - }, - - _toFocusOnNavigationComplete: (undefined: any), - - _handleFocusRequest: function(item: any) { - if (this.state.makingNavigatorRequest) { - this._toFocusOnNavigationComplete = item; - } else { - this._getFocusEmitter().emit('focus', item); - } - }, - - _focusEmitter: (undefined: ?EventEmitter), - - _getFocusEmitter: function(): EventEmitter { - // Flow not yet tracking assignments to instance fields. - let focusEmitter = this._focusEmitter; - if (!focusEmitter) { - focusEmitter = new EventEmitter(); - this._focusEmitter = focusEmitter; - } - return focusEmitter; - }, - - getChildContext: function(): { - onFocusRequested: Function, - focusEmitter: EventEmitter, - } { - return { - onFocusRequested: this._handleFocusRequest, - focusEmitter: this._getFocusEmitter(), - }; - }, - - childContextTypes: { - onFocusRequested: PropTypes.func, - focusEmitter: PropTypes.instanceOf(EventEmitter), - }, - - _tryLockNavigator: function(cb: () => void) { - this.refs[TRANSITIONER_REF].requestSchedulingNavigation( - acquiredLock => acquiredLock && cb(), - ); - }, - - _handleNavigatorStackChanged: function(e: Event) { - const newObservedTopOfStack = e.nativeEvent.stackLength - 1; - - invariant( - newObservedTopOfStack <= this.state.requestedTopOfStack, - 'No navigator item should be pushed without JS knowing about it %s %s', - newObservedTopOfStack, - this.state.requestedTopOfStack, - ); - const wasWaitingForConfirmation = - this.state.requestedTopOfStack !== this.state.observedTopOfStack; - if (wasWaitingForConfirmation) { - invariant( - newObservedTopOfStack === this.state.requestedTopOfStack, - 'If waiting for observedTopOfStack to reach requestedTopOfStack, ' + - 'the only valid observedTopOfStack should be requestedTopOfStack.', - ); - } - // Mark the most recent observation regardless of if we can lock the - // navigator. `observedTopOfStack` merely represents what we've observed - // and this first `setState` is only executed to update debugging - // overlays/navigation bar. - // Also reset progress, toIndex, and fromIndex as they might not end - // in the correct states for a two possible reasons: - // Progress isn't always 0 or 1 at the end, the system rounds - // If the Navigator is offscreen these values won't be updated - // TOOD: Revisit this decision when no longer relying on native navigator. - const nextState = { - observedTopOfStack: newObservedTopOfStack, - makingNavigatorRequest: false, - updatingAllIndicesAtOrBeyond: null, - progress: 1, - toIndex: newObservedTopOfStack, - fromIndex: newObservedTopOfStack, - }; - this.setState(nextState, this._eliminateUnneededChildren); - }, - - _eliminateUnneededChildren: function() { - // Updating the indices that we're deleting and that's all. (Truth: Nothing - // even uses the indices in this case, but let's make this describe the - // truth anyways). - const updatingAllIndicesAtOrBeyond = - this.state.routeStack.length > this.state.observedTopOfStack + 1 - ? this.state.observedTopOfStack + 1 - : null; - this.setState({ - idStack: this.state.idStack.slice(0, this.state.observedTopOfStack + 1), - routeStack: this.state.routeStack.slice( - 0, - this.state.observedTopOfStack + 1, - ), - // Now we rerequest the top of stack that we observed. - requestedTopOfStack: this.state.observedTopOfStack, - makingNavigatorRequest: true, - updatingAllIndicesAtOrBeyond: updatingAllIndicesAtOrBeyond, - }); - }, - - /** - * Navigate forward to a new route. - * @param route The new route to navigate to. - */ - push: function(route: Route) { - invariant(!!route, 'Must supply route to push'); - // Make sure all previous requests are caught up first. Otherwise reject. - if (this.state.requestedTopOfStack === this.state.observedTopOfStack) { - this._tryLockNavigator(() => { - const nextStack = this.state.routeStack.concat([route]); - const nextIDStack = this.state.idStack.concat([getuid()]); - this.setState({ - // We have to make sure that we've also supplied enough views to - // satisfy our request to adjust the `requestedTopOfStack`. - idStack: nextIDStack, - routeStack: nextStack, - requestedTopOfStack: nextStack.length - 1, - makingNavigatorRequest: true, - updatingAllIndicesAtOrBeyond: nextStack.length - 1, - }); - }); - } - }, - - /** - * Go back N scenes at once. When N=1, behavior matches `pop()`. - * @param n The number of scenes to pop. - */ - popN: function(n: number) { - if (n === 0) { - return; - } - // Make sure all previous requests are caught up first. Otherwise reject. - if (this.state.requestedTopOfStack === this.state.observedTopOfStack) { - if (this.state.requestedTopOfStack > 0) { - this._tryLockNavigator(() => { - const newRequestedTopOfStack = this.state.requestedTopOfStack - n; - invariant(newRequestedTopOfStack >= 0, 'Cannot pop below 0'); - this.setState({ - requestedTopOfStack: newRequestedTopOfStack, - makingNavigatorRequest: true, - updatingAllIndicesAtOrBeyond: this.state.requestedTopOfStack - n, - }); - }); - } - } - }, - - /** - * Pop back to the previous scene. - */ - pop: function() { - this.popN(1); - }, - - /** - * Replace a route in the navigation stack. - * - * @param route The new route that will replace the specified one. - * @param index The route into the stack that should be replaced. - * If it is negative, it counts from the back of the stack. - */ - replaceAtIndex: function(route: Route, index: number) { - invariant(!!route, 'Must supply route to replace'); - if (index < 0) { - index += this.state.routeStack.length; - } - - if (this.state.routeStack.length <= index) { - return; - } - - // I don't believe we need to lock for a replace since there's no - // navigation actually happening - const nextIDStack = this.state.idStack.slice(); - const nextRouteStack = this.state.routeStack.slice(); - nextIDStack[index] = getuid(); - nextRouteStack[index] = route; - - this.setState({ - idStack: nextIDStack, - routeStack: nextRouteStack, - makingNavigatorRequest: false, - updatingAllIndicesAtOrBeyond: index, - }); - }, - - /** - * Replace the route for the current scene and immediately - * load the view for the new route. - * @param route The new route to navigate to. - */ - replace: function(route: Route) { - this.replaceAtIndex(route, -1); - }, - - /** - * Replace the route/view for the previous scene. - * @param route The new route to will replace the previous scene. - */ - replacePrevious: function(route: Route) { - this.replaceAtIndex(route, -2); - }, - - /** - * Go back to the topmost item in the navigation stack. - */ - popToTop: function() { - this.popToRoute(this.state.routeStack[0]); - }, - - /** - * Go back to the item for a particular route object. - * @param route The new route to navigate to. - */ - popToRoute: function(route: Route) { - const indexOfRoute = this.state.routeStack.indexOf(route); - invariant( - indexOfRoute !== -1, - "Calling pop to route for a route that doesn't exist!", - ); - const numToPop = this.state.routeStack.length - indexOfRoute - 1; - this.popN(numToPop); - }, - - /** - * Replaces the previous route/view and transitions back to it. - * @param route The new route that replaces the previous scene. - */ - replacePreviousAndPop: function(route: Route) { - // Make sure all previous requests are caught up first. Otherwise reject. - if (this.state.requestedTopOfStack !== this.state.observedTopOfStack) { - return; - } - if (this.state.routeStack.length < 2) { - return; - } - this._tryLockNavigator(() => { - this.replacePrevious(route); - this.setState({ - requestedTopOfStack: this.state.requestedTopOfStack - 1, - makingNavigatorRequest: true, - }); - }); - }, - - /** - * Replaces the top item and pop to it. - * @param route The new route that will replace the topmost item. - */ - resetTo: function(route: Route) { - invariant(!!route, 'Must supply route to push'); - // Make sure all previous requests are caught up first. Otherwise reject. - if (this.state.requestedTopOfStack !== this.state.observedTopOfStack) { - return; - } - this.replaceAtIndex(route, 0); - this.popToRoute(route); - }, - - _handleNavigationComplete: function(e: Event) { - // Don't propagate to other NavigatorIOS instances this is nested in: - e.stopPropagation(); - - if (this._toFocusOnNavigationComplete) { - this._getFocusEmitter().emit('focus', this._toFocusOnNavigationComplete); - this._toFocusOnNavigationComplete = null; - } - this._handleNavigatorStackChanged(e); - }, - - _routeToStackItem: function(routeArg: Route, i: number) { - const {component, wrapperStyle, passProps, ...route} = routeArg; - const {itemWrapperStyle, ...props} = this.props; - const shouldUpdateChild = - this.state.updatingAllIndicesAtOrBeyond != null && - this.state.updatingAllIndicesAtOrBeyond >= i; - const Component = component; - return ( - - - - - - ); - }, - - _renderNavigationStackItems: function() { - const shouldRecurseToNavigator = - this.state.makingNavigatorRequest || - this.state.updatingAllIndicesAtOrBeyond !== null; - // If not recursing update to navigator at all, may as well avoid - // computation of navigator children. - const items = shouldRecurseToNavigator - ? this.state.routeStack.map(this._routeToStackItem) - : null; - return ( - - =0.41.0) - vertical={this.props.vertical} - requestedTopOfStack={this.state.requestedTopOfStack} - onNavigationComplete={this._handleNavigationComplete} - interactivePopGestureEnabled={ - this.props.interactivePopGestureEnabled - }> - {items} - - - ); - }, - - _tvEventHandler: (undefined: ?TVEventHandler), - - _enableTVEventHandler: function() { - this._tvEventHandler = new TVEventHandler(); - this._tvEventHandler.enable(this, function(cmp, evt) { - if (evt && evt.eventType === 'menu') { - cmp.pop(); - } - }); - }, - - _disableTVEventHandler: function() { - if (this._tvEventHandler) { - this._tvEventHandler.disable(); - delete this._tvEventHandler; - } - }, - - render: function() { - return ( - // $FlowFixMe(>=0.41.0) - {this._renderNavigationStackItems()} - ); - }, -}); - -const styles = StyleSheet.create({ - stackItem: { - backgroundColor: 'white', - overflow: 'hidden', - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - }, - transitioner: { - flex: 1, - }, -}); - -const RCTNavigator = requireNativeComponent('RCTNavigator'); -const RCTNavigatorItem = requireNativeComponent('RCTNavItem'); - -module.exports = NavigatorIOS; diff --git a/Libraries/react-native/react-native-implementation.js b/Libraries/react-native/react-native-implementation.js index 03d25f0fc..ff41d684b 100644 --- a/Libraries/react-native/react-native-implementation.js +++ b/Libraries/react-native/react-native-implementation.js @@ -66,9 +66,6 @@ const ReactNative = { get Modal() { return require('Modal'); }, - get NavigatorIOS() { - return require('NavigatorIOS'); - }, get Picker() { return require('Picker'); }, @@ -325,6 +322,13 @@ const ReactNative = { 'Learn about alternative navigation solutions at http://facebook.github.io/react-native/docs/navigation.html', ); }, + get NavigatorIOS() { + invariant( + false, + 'NavigatorIOS is deprecated and has been removed from this package. ' + + 'Learn about alternative navigation solutions at http://facebook.github.io/react-native/docs/navigation.html', + ); + }, }; module.exports = ReactNative; diff --git a/RNTester/js/NavigatorIOSBarStyleExample.js b/RNTester/js/NavigatorIOSBarStyleExample.js deleted file mode 100644 index 07ca90edc..000000000 --- a/RNTester/js/NavigatorIOSBarStyleExample.js +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright (c) 2013-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * @format - */ - -'use strict'; - -var React = require('react'); -var ReactNative = require('react-native'); -var {NavigatorIOS, StatusBar, StyleSheet, Text, View} = ReactNative; - -class EmptyPage extends React.Component<{ - text: string, -}> { - render() { - return ( - - {this.props.text} - - ); - } -} - -class NavigatorIOSColors extends React.Component<{}> { - static title = ' - Custom Bar Style'; - static description = 'iOS navigation with custom nav bar colors'; - - render() { - // Set StatusBar with light contents to get better contrast - StatusBar.setBarStyle('light-content'); - - return ( - ', - rightButtonTitle: 'Done', - onRightButtonPress: () => { - StatusBar.setBarStyle('default'); - this.props.onExampleExit(); - }, - passProps: { - text: 'The nav bar is black with barStyle prop.', - }, - }} - barStyle="black" - /> - ); - } -} - -var styles = StyleSheet.create({ - container: { - flex: 1, - }, - emptyPage: { - flex: 1, - paddingTop: 64, - }, - emptyPageText: { - margin: 10, - }, -}); - -NavigatorIOSColors.external = true; - -module.exports = NavigatorIOSColors; diff --git a/RNTester/js/NavigatorIOSColorsExample.js b/RNTester/js/NavigatorIOSColorsExample.js deleted file mode 100644 index d9a7cbb29..000000000 --- a/RNTester/js/NavigatorIOSColorsExample.js +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - */ - -'use strict'; - -var React = require('react'); -var ReactNative = require('react-native'); -var {NavigatorIOS, StatusBar, StyleSheet, Text, View} = ReactNative; - -class EmptyPage extends React.Component { - render() { - return ( - - {this.props.text} - - ); - } -} - -class NavigatorIOSColors extends React.Component { - static title = ' - Custom Colors'; - static description = 'iOS navigation with custom nav bar colors'; - - render() { - // Set StatusBar with light contents to get better contrast - StatusBar.setBarStyle('light-content'); - - return ( - ', - rightButtonTitle: 'Done', - onRightButtonPress: () => { - StatusBar.setBarStyle('default'); - this.props.onExampleExit(); - }, - passProps: { - text: - 'The nav bar has custom colors with tintColor, ' + - 'barTintColor and titleTextColor props.', - }, - }} - tintColor="#FFFFFF" - barTintColor="#183E63" - titleTextColor="#FFFFFF" - translucent={true} - /> - ); - } -} - -var styles = StyleSheet.create({ - container: { - flex: 1, - }, - emptyPage: { - flex: 1, - paddingTop: 64, - }, - emptyPageText: { - margin: 10, - }, -}); - -NavigatorIOSColors.external = true; - -module.exports = NavigatorIOSColors; diff --git a/RNTester/js/NavigatorIOSExample.js b/RNTester/js/NavigatorIOSExample.js deleted file mode 100644 index 252bf0e9e..000000000 --- a/RNTester/js/NavigatorIOSExample.js +++ /dev/null @@ -1,303 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - * @flow - */ - -'use strict'; - -const React = require('react'); -const ReactNative = require('react-native'); -const ViewExample = require('./ViewExample'); - -const createExamplePage = require('./createExamplePage'); -const nativeImageSource = require('nativeImageSource'); -const { - AlertIOS, - NavigatorIOS, - ScrollView, - StyleSheet, - Text, - TouchableHighlight, - View, -} = ReactNative; - -class EmptyPage extends React.Component<$FlowFixMeProps> { - render() { - return ( - - {this.props.text} - - ); - } -} - -class NavigatorIOSExamplePage extends React.Component<$FlowFixMeProps> { - render() { - var recurseTitle = 'Recurse Navigation'; - if (!this.props.depth || this.props.depth === 1) { - recurseTitle += ' - more examples here'; - } - return ( - - - - {this._renderRow(recurseTitle, () => { - this.props.navigator.push({ - title: NavigatorIOSExample.title, - component: NavigatorIOSExamplePage, - backButtonTitle: 'Custom Back', - passProps: {depth: this.props.depth ? this.props.depth + 1 : 1}, - }); - })} - {this._renderRow('Push View Example', () => { - this.props.navigator.push({ - title: 'Very Long Custom View Example Title', - component: createExamplePage(null, ViewExample), - }); - })} - {this._renderRow('Custom title image Example', () => { - this.props.navigator.push({ - title: 'Custom title image Example', - titleImage: require('./relay.png'), - component: createExamplePage(null, ViewExample), - }); - })} - {this._renderRow('Custom Right Button', () => { - this.props.navigator.push({ - title: NavigatorIOSExample.title, - component: EmptyPage, - rightButtonTitle: 'Cancel', - onRightButtonPress: () => this.props.navigator.pop(), - passProps: { - text: 'This page has a right button in the nav bar', - }, - }); - })} - {this._renderRow('Custom Right System Button', () => { - this.props.navigator.push({ - title: NavigatorIOSExample.title, - component: EmptyPage, - rightButtonSystemIcon: 'bookmarks', - onRightButtonPress: () => this.props.navigator.pop(), - passProps: { - text: 'This page has a right system button in the nav bar', - }, - }); - })} - {this._renderRow('Custom Left & Right Icons', () => { - this.props.navigator.push({ - title: NavigatorIOSExample.title, - component: EmptyPage, - leftButtonTitle: 'Custom Left', - onLeftButtonPress: () => this.props.navigator.pop(), - rightButtonIcon: nativeImageSource({ - ios: 'NavBarButtonPlus', - width: 17, - height: 17, - }), - onRightButtonPress: () => { - AlertIOS.alert( - 'Bar Button Action', - 'Recognized a tap on the bar button icon', - [ - { - text: 'OK', - onPress: () => console.log('Tapped OK'), - }, - ], - ); - }, - passProps: { - text: - 'This page has an icon for the right button in the nav bar', - }, - }); - })} - {this._renderRow('Custom Left & Right System Icons', () => { - this.props.navigator.push({ - title: NavigatorIOSExample.title, - component: EmptyPage, - leftButtonSystemIcon: 'cancel', - onLeftButtonPress: () => this.props.navigator.pop(), - rightButtonSystemIcon: 'search', - onRightButtonPress: () => { - AlertIOS.alert( - 'Bar Button Action', - 'Recognized a tap on the bar button icon', - [ - { - text: 'OK', - onPress: () => console.log('Tapped OK'), - }, - ], - ); - }, - passProps: { - text: - 'This page has an icon for the right button in the nav bar', - }, - }); - })} - {this._renderRow('Pop', () => { - this.props.navigator.pop(); - })} - {this._renderRow('Pop to top', () => { - this.props.navigator.popToTop(); - })} - {this._renderReplace()} - {this._renderReplacePrevious()} - {this._renderReplacePreviousAndPop()} - {this._renderRow( - 'Exit NavigatorIOS Example', - this.props.onExampleExit, - )} - - - - ); - } - - _renderReplace = () => { - if (!this.props.depth) { - // this is to avoid replacing the top of the stack - return null; - } - return this._renderRow('Replace here', () => { - var prevRoute = this.props.route; - this.props.navigator.replace({ - title: 'New Navigation', - component: EmptyPage, - rightButtonTitle: 'Undo', - onRightButtonPress: () => this.props.navigator.replace(prevRoute), - passProps: { - text: - 'The component is replaced, but there is currently no ' + - 'way to change the right button or title of the current route', - }, - }); - }); - }; - - _renderReplacePrevious = () => { - if (!this.props.depth || this.props.depth < 2) { - // this is to avoid replacing the top of the stack - return null; - } - return this._renderRow('Replace previous', () => { - this.props.navigator.replacePrevious({ - title: 'Replaced', - component: EmptyPage, - passProps: { - text: 'This is a replaced "previous" page', - }, - wrapperStyle: styles.customWrapperStyle, - }); - }); - }; - - _renderReplacePreviousAndPop = () => { - if (!this.props.depth || this.props.depth < 2) { - // this is to avoid replacing the top of the stack - return null; - } - return this._renderRow('Replace previous and pop', () => { - this.props.navigator.replacePreviousAndPop({ - title: 'Replaced and Popped', - component: EmptyPage, - passProps: { - text: 'This is a replaced "previous" page', - }, - wrapperStyle: styles.customWrapperStyle, - }); - }); - }; - - _renderRow = (title: string, onPress: Function) => { - return ( - - - - {title} - - - - - ); - }; -} - -class NavigatorIOSExample extends React.Component<$FlowFixMeProps> { - static title = ''; - static description = 'iOS navigation capabilities'; - static external = true; - - render() { - const {onExampleExit} = this.props; - return ( - - ); - } -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, - customWrapperStyle: { - backgroundColor: '#bbdddd', - }, - emptyPage: { - flex: 1, - paddingTop: 64, - }, - emptyPageText: { - margin: 10, - }, - list: { - backgroundColor: '#eeeeee', - marginTop: 10, - }, - group: { - backgroundColor: 'white', - }, - groupSpace: { - height: 15, - }, - line: { - backgroundColor: '#bbbbbb', - height: StyleSheet.hairlineWidth, - }, - row: { - backgroundColor: 'white', - justifyContent: 'center', - paddingHorizontal: 15, - paddingVertical: 15, - }, - separator: { - height: StyleSheet.hairlineWidth, - backgroundColor: '#bbbbbb', - marginLeft: 15, - }, - rowNote: { - fontSize: 17, - }, - rowText: { - fontSize: 17, - fontWeight: '500', - }, -}); - -module.exports = NavigatorIOSExample; diff --git a/RNTester/js/RNTesterList.ios.js b/RNTester/js/RNTesterList.ios.js index f7a62efaa..bd717ea7d 100644 --- a/RNTester/js/RNTesterList.ios.js +++ b/RNTester/js/RNTesterList.ios.js @@ -92,21 +92,6 @@ const ComponentExamples: Array = [ module: require('./MultiColumnExample'), supportsTVOS: true, }, - { - key: 'NavigatorIOSColorsExample', - module: require('./NavigatorIOSColorsExample'), - supportsTVOS: false, - }, - { - key: 'NavigatorIOSBarStyleExample', - module: require('./NavigatorIOSBarStyleExample'), - supportsTVOS: false, - }, - { - key: 'NavigatorIOSExample', - module: require('./NavigatorIOSExample'), - supportsTVOS: true, - }, { key: 'PickerExample', module: require('./PickerExample'), diff --git a/React/Views/RCTNavItem.h b/React/Views/RCTNavItem.h deleted file mode 100644 index 9cabdf2e6..000000000 --- a/React/Views/RCTNavItem.h +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import - -#import - -@interface RCTNavItem : UIView - -@property (nonatomic, copy) NSString *title; -@property (nonatomic, strong) UIImage *titleImage; -@property (nonatomic, strong) UIImage *leftButtonIcon; -@property (nonatomic, copy) NSString *leftButtonTitle; -@property (nonatomic, assign) UIBarButtonSystemItem leftButtonSystemIcon; -@property (nonatomic, strong) UIImage *rightButtonIcon; -@property (nonatomic, copy) NSString *rightButtonTitle; -@property (nonatomic, assign) UIBarButtonSystemItem rightButtonSystemIcon; -@property (nonatomic, strong) UIImage *backButtonIcon; -@property (nonatomic, copy) NSString *backButtonTitle; -@property (nonatomic, assign) BOOL navigationBarHidden; -@property (nonatomic, assign) BOOL shadowHidden; -@property (nonatomic, strong) UIColor *tintColor; -@property (nonatomic, strong) UIColor *barTintColor; -@property (nonatomic, strong) UIColor *titleTextColor; -@property (nonatomic, assign) BOOL translucent; -#if !TARGET_OS_TV -@property (nonatomic, assign) UIBarStyle barStyle; -#endif - -@property (nonatomic, readonly) UIImageView *titleImageView; -@property (nonatomic, readonly) UIBarButtonItem *backButtonItem; -@property (nonatomic, readonly) UIBarButtonItem *leftButtonItem; -@property (nonatomic, readonly) UIBarButtonItem *rightButtonItem; - -@property (nonatomic, copy) RCTBubblingEventBlock onLeftButtonPress; -@property (nonatomic, copy) RCTBubblingEventBlock onRightButtonPress; - -@end diff --git a/React/Views/RCTNavItem.m b/React/Views/RCTNavItem.m deleted file mode 100644 index 58923e245..000000000 --- a/React/Views/RCTNavItem.m +++ /dev/null @@ -1,174 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import "RCTNavItem.h" - -@implementation RCTNavItem - -@synthesize backButtonItem = _backButtonItem; -@synthesize leftButtonItem = _leftButtonItem; -@synthesize rightButtonItem = _rightButtonItem; - -- (UIImageView *)titleImageView -{ - if (_titleImage) { - return [[UIImageView alloc] initWithImage:_titleImage]; - } else { - return nil; - } -} - --(instancetype)init -{ - if (self = [super init]) { - _leftButtonSystemIcon = NSNotFound; - _rightButtonSystemIcon = NSNotFound; - } - return self; -} - -- (void)setBackButtonTitle:(NSString *)backButtonTitle -{ - _backButtonTitle = backButtonTitle; - _backButtonItem = nil; -} - -- (void)setBackButtonIcon:(UIImage *)backButtonIcon -{ - _backButtonIcon = backButtonIcon; - _backButtonItem = nil; -} - -- (UIBarButtonItem *)backButtonItem -{ - if (!_backButtonItem) { - if (_backButtonIcon) { - _backButtonItem = [[UIBarButtonItem alloc] initWithImage:_backButtonIcon - style:UIBarButtonItemStylePlain - target:nil - action:nil]; - } else if (_backButtonTitle.length) { - _backButtonItem = [[UIBarButtonItem alloc] initWithTitle:_backButtonTitle - style:UIBarButtonItemStylePlain - target:nil - action:nil]; - } else { - _backButtonItem = nil; - } - } - return _backButtonItem; -} - -- (void)setLeftButtonTitle:(NSString *)leftButtonTitle -{ - _leftButtonTitle = leftButtonTitle; - _leftButtonItem = nil; -} - -- (void)setLeftButtonIcon:(UIImage *)leftButtonIcon -{ - _leftButtonIcon = leftButtonIcon; - _leftButtonItem = nil; -} - -- (void)setLeftButtonSystemIcon:(UIBarButtonSystemItem)leftButtonSystemIcon -{ - _leftButtonSystemIcon = leftButtonSystemIcon; - _leftButtonItem = nil; -} - -- (UIBarButtonItem *)leftButtonItem -{ - if (!_leftButtonItem) { - if (_leftButtonIcon) { - _leftButtonItem = - [[UIBarButtonItem alloc] initWithImage:_leftButtonIcon - style:UIBarButtonItemStylePlain - target:self - action:@selector(handleLeftButtonPress)]; - - } else if (_leftButtonTitle.length) { - _leftButtonItem = - [[UIBarButtonItem alloc] initWithTitle:_leftButtonTitle - style:UIBarButtonItemStylePlain - target:self - action:@selector(handleLeftButtonPress)]; - - } else if (_leftButtonSystemIcon != NSNotFound) { - _leftButtonItem = - [[UIBarButtonItem alloc] initWithBarButtonSystemItem:_leftButtonSystemIcon - target:self - action:@selector(handleLeftButtonPress)]; - } else { - _leftButtonItem = nil; - } - } - return _leftButtonItem; -} - -- (void)handleLeftButtonPress -{ - if (_onLeftButtonPress) { - _onLeftButtonPress(nil); - } -} - -- (void)setRightButtonTitle:(NSString *)rightButtonTitle -{ - _rightButtonTitle = rightButtonTitle; - _rightButtonItem = nil; -} - -- (void)setRightButtonIcon:(UIImage *)rightButtonIcon -{ - _rightButtonIcon = rightButtonIcon; - _rightButtonItem = nil; -} - -- (void)setRightButtonSystemIcon:(UIBarButtonSystemItem)rightButtonSystemIcon -{ - _rightButtonSystemIcon = rightButtonSystemIcon; - _rightButtonItem = nil; -} - -- (UIBarButtonItem *)rightButtonItem -{ - if (!_rightButtonItem) { - if (_rightButtonIcon) { - _rightButtonItem = - [[UIBarButtonItem alloc] initWithImage:_rightButtonIcon - style:UIBarButtonItemStylePlain - target:self - action:@selector(handleRightButtonPress)]; - - } else if (_rightButtonTitle.length) { - _rightButtonItem = - [[UIBarButtonItem alloc] initWithTitle:_rightButtonTitle - style:UIBarButtonItemStylePlain - target:self - action:@selector(handleRightButtonPress)]; - - } else if (_rightButtonSystemIcon != NSNotFound) { - _rightButtonItem = - [[UIBarButtonItem alloc] initWithBarButtonSystemItem:_rightButtonSystemIcon - target:self - action:@selector(handleRightButtonPress)]; - } else { - _rightButtonItem = nil; - } - } - return _rightButtonItem; -} - -- (void)handleRightButtonPress -{ - if (_onRightButtonPress) { - _onRightButtonPress(nil); - } -} - -@end diff --git a/React/Views/RCTNavItemManager.h b/React/Views/RCTNavItemManager.h deleted file mode 100644 index 8fcb61c36..000000000 --- a/React/Views/RCTNavItemManager.h +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import -#import - -@interface RCTConvert (BarButtonSystemItem) - -+ (UIBarButtonSystemItem)UIBarButtonSystemItem:(id)json; - -@end - -@interface RCTNavItemManager : RCTViewManager - -@end diff --git a/React/Views/RCTNavItemManager.m b/React/Views/RCTNavItemManager.m deleted file mode 100644 index d9d349c5a..000000000 --- a/React/Views/RCTNavItemManager.m +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import "RCTNavItemManager.h" - -#import "RCTConvert.h" -#import "RCTNavItem.h" - -@implementation RCTConvert (BarButtonSystemItem) - -RCT_ENUM_CONVERTER(UIBarButtonSystemItem, (@{ - @"done": @(UIBarButtonSystemItemDone), - @"cancel": @(UIBarButtonSystemItemCancel), - @"edit": @(UIBarButtonSystemItemEdit), - @"save": @(UIBarButtonSystemItemSave), - @"add": @(UIBarButtonSystemItemAdd), - @"flexible-space": @(UIBarButtonSystemItemFlexibleSpace), - @"fixed-space": @(UIBarButtonSystemItemFixedSpace), - @"compose": @(UIBarButtonSystemItemCompose), - @"reply": @(UIBarButtonSystemItemReply), - @"action": @(UIBarButtonSystemItemAction), - @"organize": @(UIBarButtonSystemItemOrganize), - @"bookmarks": @(UIBarButtonSystemItemBookmarks), - @"search": @(UIBarButtonSystemItemSearch), - @"refresh": @(UIBarButtonSystemItemRefresh), - @"stop": @(UIBarButtonSystemItemStop), - @"camera": @(UIBarButtonSystemItemCamera), - @"trash": @(UIBarButtonSystemItemTrash), - @"play": @(UIBarButtonSystemItemPlay), - @"pause": @(UIBarButtonSystemItemPause), - @"rewind": @(UIBarButtonSystemItemRewind), - @"fast-forward": @(UIBarButtonSystemItemFastForward), - @"undo": @(UIBarButtonSystemItemUndo), - @"redo": @(UIBarButtonSystemItemRedo), - @"page-curl": @(UIBarButtonSystemItemPageCurl) -}), NSNotFound, integerValue); - -@end - -@implementation RCTNavItemManager - -RCT_EXPORT_MODULE() - -- (UIView *)view -{ - return [RCTNavItem new]; -} - -RCT_EXPORT_VIEW_PROPERTY(navigationBarHidden, BOOL) -RCT_EXPORT_VIEW_PROPERTY(shadowHidden, BOOL) -RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor) -RCT_EXPORT_VIEW_PROPERTY(barTintColor, UIColor) -#if !TARGET_OS_TV -RCT_EXPORT_VIEW_PROPERTY(barStyle, UIBarStyle) -#endif -RCT_EXPORT_VIEW_PROPERTY(translucent, BOOL) - -RCT_EXPORT_VIEW_PROPERTY(title, NSString) -RCT_EXPORT_VIEW_PROPERTY(titleTextColor, UIColor) -RCT_EXPORT_VIEW_PROPERTY(titleImage, UIImage) - -RCT_EXPORT_VIEW_PROPERTY(backButtonIcon, UIImage) -RCT_EXPORT_VIEW_PROPERTY(backButtonTitle, NSString) - -RCT_EXPORT_VIEW_PROPERTY(leftButtonTitle, NSString) -RCT_EXPORT_VIEW_PROPERTY(leftButtonIcon, UIImage) -RCT_EXPORT_VIEW_PROPERTY(leftButtonSystemIcon, UIBarButtonSystemItem) - -RCT_EXPORT_VIEW_PROPERTY(rightButtonIcon, UIImage) -RCT_EXPORT_VIEW_PROPERTY(rightButtonTitle, NSString) -RCT_EXPORT_VIEW_PROPERTY(rightButtonSystemIcon, UIBarButtonSystemItem) - -RCT_EXPORT_VIEW_PROPERTY(onLeftButtonPress, RCTBubblingEventBlock) -RCT_EXPORT_VIEW_PROPERTY(onRightButtonPress, RCTBubblingEventBlock) - -@end diff --git a/React/Views/RCTNavigator.h b/React/Views/RCTNavigator.h deleted file mode 100644 index b8ad30141..000000000 --- a/React/Views/RCTNavigator.h +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import - -#import - -@class RCTBridge; - -@interface RCTNavigator : UIView - -@property (nonatomic, strong) UIView *reactNavSuperviewLink; -@property (nonatomic, assign) NSInteger requestedTopOfStack; -@property (nonatomic, assign) BOOL interactivePopGestureEnabled; - -- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER; - -/** - * Schedules a JavaScript navigation and prevents `UIKit` from navigating until - * JavaScript has sent its scheduled navigation. - * - * @returns Whether or not a JavaScript driven navigation could be - * scheduled/reserved. If returning `NO`, JavaScript should usually just do - * nothing at all. - */ -- (BOOL)requestSchedulingJavaScriptNavigation; - -- (void)uiManagerDidPerformMounting; - -@end diff --git a/React/Views/RCTNavigator.m b/React/Views/RCTNavigator.m deleted file mode 100644 index 0a0bfe61c..000000000 --- a/React/Views/RCTNavigator.m +++ /dev/null @@ -1,630 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import "RCTNavigator.h" - -#import "RCTAssert.h" -#import "RCTBridge.h" -#import "RCTConvert.h" -#import "RCTEventDispatcher.h" -#import "RCTLog.h" -#import "RCTNavItem.h" -#import "RCTScrollView.h" -#import "RCTUtils.h" -#import "RCTView.h" -#import "RCTWrapperViewController.h" -#import "UIView+React.h" - -typedef NS_ENUM(NSUInteger, RCTNavigationLock) { - RCTNavigationLockNone, - RCTNavigationLockNative, - RCTNavigationLockJavaScript -}; - -// By default the interactive pop gesture will be enabled when the navigation bar is displayed -// and disabled when hidden -// RCTPopGestureStateDefault maps to the default behavior (mentioned above). Once popGestureState -// leaves this value, it can never be returned back to it. This is because, due to a limitation in -// the iOS APIs, once we override the default behavior of the gesture recognizer, we cannot return -// back to it. -// RCTPopGestureStateEnabled will enable the gesture independent of nav bar visibility -// RCTPopGestureStateDisabled will disable the gesture independent of nav bar visibility -typedef NS_ENUM(NSUInteger, RCTPopGestureState) { - RCTPopGestureStateDefault = 0, - RCTPopGestureStateEnabled, - RCTPopGestureStateDisabled -}; - -NSInteger kNeverRequested = -1; -NSInteger kNeverProgressed = -10000; - - -@interface UINavigationController () - -// need to declare this since `UINavigationController` doesn't publicly declare the fact that it implements -// UINavigationBarDelegate :( -- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item; - -@end - -// http://stackoverflow.com/questions/5115135/uinavigationcontroller-how-to-cancel-the-back-button-event -// There's no other way to do this unfortunately :( -@interface RCTNavigationController : UINavigationController -{ - dispatch_block_t _scrollCallback; -} - -@property (nonatomic, assign) RCTNavigationLock navigationLock; - -@end - -/** - * In general, `RCTNavigator` examines `_currentViews` (which are React child - * views), and compares them to `_navigationController.viewControllers` (which - * are controlled by UIKit). - * - * It is possible for JavaScript (`_currentViews`) to "get ahead" of native - * (`navigationController.viewControllers`) and vice versa. JavaScript gets - * ahead by adding/removing React subviews. Native gets ahead by swiping back, - * or tapping the back button. In both cases, the other system is initially - * unaware. And in both cases, `RCTNavigator` helps the other side "catch up". - * - * If `RCTNavigator` sees the number of React children have changed, it - * pushes/pops accordingly. If `RCTNavigator` sees a `UIKit` driven push/pop, it - * notifies JavaScript that this has happened, and expects that JavaScript will - * eventually render more children to match `UIKit`. There's no rush for - * JavaScript to catch up. But if it does render anything, it must catch up to - * UIKit. It cannot deviate. - * - * To implement this, we need a lock, which we store on the native thread. This - * lock allows one of the systems to push/pop views. Whoever wishes to - * "get ahead" must obtain the lock. Whoever wishes to "catch up" must obtain - * the lock. One thread may not "get ahead" or "catch up" when the other has - * the lock. Once a thread has the lock, it can only do the following: - * - * 1. If it is behind, it may only catch up. - * 2. If it is caught up or ahead, it may push or pop. - * - * - * ========= Acquiring The Lock ========== - * - * JavaScript asynchronously acquires the lock using a native hook. It might be - * rejected and receive the return value `false`. - * - * We acquire the native lock in `shouldPopItem`, which is called right before - * native tries to push/pop, but only if JavaScript doesn't already have the - * lock. - * - * ======== While JavaScript Has Lock ==== - * - * When JavaScript has the lock, we have to block all `UIKit` driven pops: - * - * 1. Block back button navigation: - * - Back button will invoke `shouldPopItem`, from which we return `NO` if - * JavaScript has the lock. - * - Back button will respect the return value `NO` and not permit - * navigation. - * - * 2. Block swipe-to-go-back navigation: - * - Swipe will trigger `shouldPopItem`, but swipe won't respect our `NO` - * return value so we must disable the gesture recognizer while JavaScript - * has the lock. - * - * ======== While Native Has Lock ======= - * - * We simply deny JavaScript the right to acquire the lock. - * - * - * ======== Releasing The Lock =========== - * - * Recall that the lock represents who has the right to either push/pop (or - * catch up). As soon as we recognize that the side that has locked has carried - * out what it scheduled to do, we can release the lock, but only after any - * possible animations are completed. - * - * *IF* a scheduled operation results in a push/pop (not all do), then we can - * only release the lock after the push/pop animation is complete because - * UIKit. `didMoveToNavigationController` is invoked when the view is done - * pushing/popping/animating. Native swipe-to-go-back interactions can be - * aborted, however, and you'll never see that method invoked. So just to cover - * that case, we also put an animation complete hook in - * `animateAlongsideTransition` to make sure we free the lock, in case the - * scheduled native push/pop never actually happened. - * - * For JavaScript: - * - When we see that JavaScript has "caught up" to `UIKit`, and no pushes/pops - * were needed, we can release the lock. - * - When we see that JavaScript requires *some* push/pop, it's not yet done - * carrying out what it scheduled to do. Just like with `UIKit` push/pops, we - * still have to wait for it to be done animating - * (`didMoveToNavigationController` is a suitable hook). - * - */ -@implementation RCTNavigationController - -/** - * @param callback Callback that is invoked when a "scroll" interaction begins - * so that `RCTNavigator` can notify `JavaScript`. - */ -- (instancetype)initWithScrollCallback:(dispatch_block_t)callback -{ - if ((self = [super initWithNibName:nil bundle:nil])) { - _scrollCallback = callback; - } - return self; -} - -/** - * Invoked when either a navigation item has been popped off, or when a - * swipe-back gesture has began. The swipe-back gesture doesn't respect the - * return value of this method. The back button does. That's why we have to - * completely disable the gesture recognizer for swipe-back while JS has the - * lock. - */ -- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item -{ -#if !TARGET_OS_TV - if (self.interactivePopGestureRecognizer.state == UIGestureRecognizerStateBegan) { - if (self.navigationLock == RCTNavigationLockNone) { - self.navigationLock = RCTNavigationLockNative; - if (_scrollCallback) { - _scrollCallback(); - } - } else if (self.navigationLock == RCTNavigationLockJavaScript) { - // This should never happen because we disable/enable the gesture - // recognizer when we lock the navigation. - RCTAssert(NO, @"Should never receive gesture start while JS locks navigator"); - } - } else -#endif //TARGET_OS_TV - { - if (self.navigationLock == RCTNavigationLockNone) { - // Must be coming from native interaction, lock it - it will be unlocked - // in `didMoveToNavigationController` - self.navigationLock = RCTNavigationLockNative; - if (_scrollCallback) { - _scrollCallback(); - } - } else if (self.navigationLock == RCTNavigationLockJavaScript) { - // This should only occur when JS has the lock, and - // - JS is driving the pop - // - Or the back button was pressed - // TODO: We actually want to disable the backbutton while JS has the - // lock, but it's not so easy. Even returning `NO` wont' work because it - // will also block JS driven pops. We simply need to disallow a standard - // back button, and instead use a custom one that tells JS to pop to - // length (`currentReactCount` - 1). - return [super navigationBar:navigationBar shouldPopItem:item]; - } - } - return [super navigationBar:navigationBar shouldPopItem:item]; -} - -@end - -@interface RCTNavigator() - -@property (nonatomic, copy) RCTDirectEventBlock onNavigationProgress; -@property (nonatomic, copy) RCTBubblingEventBlock onNavigationComplete; - -@property (nonatomic, assign) NSInteger previousRequestedTopOfStack; - -@property (nonatomic, assign) RCTPopGestureState popGestureState; - -// Previous views are only mainted in order to detect incorrect -// addition/removal of views below the `requestedTopOfStack` -@property (nonatomic, copy, readwrite) NSArray *previousViews; -@property (nonatomic, readwrite, strong) RCTNavigationController *navigationController; -/** - * Display link is used to get high frequency sample rate during - * interaction/animation of view controller push/pop. - * - * - The run loop retains the displayLink. - * - `displayLink` retains its target. - * - We use `invalidate` to remove the `RCTNavigator`'s reference to the - * `displayLink` and remove the `displayLink` from the run loop. - * - * - * `displayLink`: - * -------------- - * - * - Even though we could implement the `displayLink` cleanup without the - * `invalidate` hook by adding and removing it from the run loop at the - * right times (begin/end animation), we need to account for the possibility - * that the view itself is destroyed mid-interaction. So we always keep it - * added to the run loop, but start/stop it with interactions/animations. We - * remove it from the run loop when the view will be destroyed by React. - * - * +----------+ +--------------+ - * | run loop o----strong--->| displayLink | - * +----------+ +--o-----------+ - * | ^ - * | | - * strong strong - * | | - * v | - * +---------o---+ - * | RCTNavigator | - * +-------------+ - * - * `dummyView`: - * ------------ - * There's no easy way to get a callback that fires when the position of a - * navigation item changes. The actual layers that are moved around during the - * navigation transition are private. Our only hope is to use - * `animateAlongsideTransition`, to set a dummy view's position to transition - * anywhere from -1.0 to 1.0. We later set up a `CADisplayLink` to poll the - * `presentationLayer` of that dummy view and report the value as a "progress" - * percentage. - * - * It was critical that we added the dummy view as a subview of the - * transitionCoordinator's `containerView`, otherwise the animations would not - * work correctly when reversing the gesture direction etc. This seems to be - * undocumented behavior/requirement. - * - */ -@property (nonatomic, readonly, assign) CGFloat mostRecentProgress; -@property (nonatomic, readonly, strong) NSTimer *runTimer; -@property (nonatomic, readonly, assign) NSInteger currentlyTransitioningFrom; -@property (nonatomic, readonly, assign) NSInteger currentlyTransitioningTo; - -// Dummy view that we make animate with the same curve/interaction as the -// navigation animation/interaction. -@property (nonatomic, readonly, strong) UIView *dummyView; - -@end - -@implementation RCTNavigator -{ - __weak RCTBridge *_bridge; - NSInteger _numberOfViewControllerMovesToIgnore; -} - -@synthesize paused = _paused; -@synthesize pauseCallback = _pauseCallback; - -- (instancetype)initWithBridge:(RCTBridge *)bridge -{ - RCTAssertParam(bridge); - - if ((self = [super initWithFrame:CGRectZero])) { - _paused = YES; - - _bridge = bridge; - _mostRecentProgress = kNeverProgressed; - _dummyView = [[UIView alloc] initWithFrame:CGRectZero]; - _previousRequestedTopOfStack = kNeverRequested; // So that we initialize with a push. - _previousViews = @[]; - __weak RCTNavigator *weakSelf = self; - _navigationController = [[RCTNavigationController alloc] initWithScrollCallback:^{ - [weakSelf dispatchFakeScrollEvent]; - }]; - _navigationController.delegate = self; - RCTAssert([self requestSchedulingJavaScriptNavigation], @"Could not acquire JS navigation lock on init"); - - [self addSubview:_navigationController.view]; - [_navigationController.view addSubview:_dummyView]; - } - return self; -} - -RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame) -RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) - -- (void)didUpdateFrame:(__unused RCTFrameUpdate *)update -{ - if (_currentlyTransitioningFrom != _currentlyTransitioningTo) { - UIView *topView = _dummyView; - id presentationLayer = [topView.layer presentationLayer]; - CGRect frame = [presentationLayer frame]; - CGFloat nextProgress = ABS(frame.origin.x); - // Don't want to spam the bridge, when the user holds their finger still mid-navigation. - if (nextProgress == _mostRecentProgress) { - return; - } - _mostRecentProgress = nextProgress; - if (_onNavigationProgress) { - _onNavigationProgress(@{ - @"fromIndex": @(_currentlyTransitioningFrom), - @"toIndex": @(_currentlyTransitioningTo), - @"progress": @(nextProgress), - }); - } - } -} - -- (void)setPaused:(BOOL)paused -{ - if (_paused != paused) { - _paused = paused; - if (_pauseCallback) { - _pauseCallback(); - } - } -} - -- (void)setInteractivePopGestureEnabled:(BOOL)interactivePopGestureEnabled -{ -#if !TARGET_OS_TV - _interactivePopGestureEnabled = interactivePopGestureEnabled; - - _navigationController.interactivePopGestureRecognizer.delegate = self; - _navigationController.interactivePopGestureRecognizer.enabled = interactivePopGestureEnabled; - - _popGestureState = interactivePopGestureEnabled ? RCTPopGestureStateEnabled : RCTPopGestureStateDisabled; -#endif -} - -- (void)dealloc -{ -#if !TARGET_OS_TV - if (_navigationController.interactivePopGestureRecognizer.delegate == self) { - _navigationController.interactivePopGestureRecognizer.delegate = nil; - } -#endif - _navigationController.delegate = nil; - [_navigationController removeFromParentViewController]; -} - -- (UIViewController *)reactViewController -{ - return _navigationController; -} - -- (BOOL)gestureRecognizerShouldBegin:(__unused UIGestureRecognizer *)gestureRecognizer -{ - return _navigationController.viewControllers.count > 1; -} - -/** - * See documentation about lock lifecycle. This is only here to clean up - * swipe-back abort interaction, which leaves us *no* other way to clean up - * locks aside from the animation complete hook. - */ -- (void)navigationController:(UINavigationController *)navigationController - willShowViewController:(__unused UIViewController *)viewController - animated:(__unused BOOL)animated -{ - id tc = - navigationController.topViewController.transitionCoordinator; - __weak RCTNavigator *weakSelf = self; - [tc.containerView addSubview: _dummyView]; - [tc animateAlongsideTransition: ^(id context) { - RCTWrapperViewController *fromController = - (RCTWrapperViewController *)[context viewControllerForKey:UITransitionContextFromViewControllerKey]; - RCTWrapperViewController *toController = - (RCTWrapperViewController *)[context viewControllerForKey:UITransitionContextToViewControllerKey]; - - // This may be triggered by a navigation controller unrelated to me: if so, ignore. - if (fromController.navigationController != self->_navigationController || - toController.navigationController != self->_navigationController) { - return; - } - - NSUInteger indexOfFrom = [self.reactSubviews indexOfObject:fromController.navItem]; - NSUInteger indexOfTo = [self.reactSubviews indexOfObject:toController.navItem]; - CGFloat destination = indexOfFrom < indexOfTo ? 1.0 : -1.0; - self->_dummyView.frame = (CGRect){{destination, 0}, CGSizeZero}; - self->_currentlyTransitioningFrom = indexOfFrom; - self->_currentlyTransitioningTo = indexOfTo; - self.paused = NO; - } - completion:^(__unused id context) { - [weakSelf freeLock]; - self->_currentlyTransitioningFrom = 0; - self->_currentlyTransitioningTo = 0; - self->_dummyView.frame = CGRectZero; - self.paused = YES; - // Reset the parallel position tracker - }]; -} - -- (BOOL)requestSchedulingJavaScriptNavigation -{ - if (_navigationController.navigationLock == RCTNavigationLockNone) { - _navigationController.navigationLock = RCTNavigationLockJavaScript; -#if !TARGET_OS_TV - _navigationController.interactivePopGestureRecognizer.enabled = NO; -#endif - return YES; - } - return NO; -} - -- (void)freeLock -{ - _navigationController.navigationLock = RCTNavigationLockNone; - - // Unless the pop gesture has been explicitly disabled (RCTPopGestureStateDisabled), - // Set interactivePopGestureRecognizer.enabled to YES - // If the popGestureState is RCTPopGestureStateDefault the default behavior will be maintained -#if !TARGET_OS_TV - _navigationController.interactivePopGestureRecognizer.enabled = self.popGestureState != RCTPopGestureStateDisabled; -#endif -} - -/** - * A React subview can be inserted/removed at any time, however if the - * `requestedTopOfStack` changes, there had better be enough subviews present - * to satisfy the push/pop. - */ -- (void)insertReactSubview:(RCTNavItem *)view atIndex:(NSInteger)atIndex -{ - RCTAssert([view isKindOfClass:[RCTNavItem class]], @"RCTNavigator only accepts RCTNavItem subviews"); - RCTAssert( - _navigationController.navigationLock == RCTNavigationLockJavaScript, - @"Cannot change subviews from JS without first locking." - ); - [super insertReactSubview:view atIndex:atIndex]; -} - -- (void)didUpdateReactSubviews -{ - // Do nothing, as subviews are managed by `uiManagerDidPerformMounting` -} - -- (void)layoutSubviews -{ - [super layoutSubviews]; - [self reactAddControllerToClosestParent:_navigationController]; - _navigationController.view.frame = self.bounds; -} - -- (void)removeReactSubview:(RCTNavItem *)subview -{ - if (self.reactSubviews.count <= 0 || subview == self.reactSubviews[0]) { - RCTLogError(@"Attempting to remove invalid RCT subview of RCTNavigator"); - return; - } - [super removeReactSubview:subview]; -} - -- (void)handleTopOfStackChanged -{ - if (_onNavigationComplete) { - _onNavigationComplete(@{ - @"stackLength":@(_navigationController.viewControllers.count) - }); - } -} - -- (void)dispatchFakeScrollEvent -{ - [_bridge.eventDispatcher sendFakeScrollEvent:self.reactTag]; -} - -/** - * Must be overridden because UIKit removes the view's superview when used - * as a navigator - it's considered outside the view hierarchy. - */ -- (UIView *)reactSuperview -{ - RCTAssert(!_bridge.isValid || self.superview != nil, @"put reactNavSuperviewLink back"); - UIView *superview = [super reactSuperview]; - return superview ?: self.reactNavSuperviewLink; -} - -- (void)uiManagerDidPerformMounting -{ - // we can't hook up the VC hierarchy in 'init' because the subviews aren't - // hooked up yet, so we do it on demand here - [self reactAddControllerToClosestParent:_navigationController]; - - NSUInteger viewControllerCount = _navigationController.viewControllers.count; - // The "react count" is the count of views that are visible on the navigation - // stack. There may be more beyond this - that aren't visible, and may be - // deleted/purged soon. - NSUInteger previousReactCount = - _previousRequestedTopOfStack == kNeverRequested ? 0 : _previousRequestedTopOfStack + 1; - NSUInteger currentReactCount = _requestedTopOfStack + 1; - - BOOL jsGettingAhead = - // ----- previously caught up ------ ------ no longer caught up ------- - viewControllerCount == previousReactCount && currentReactCount != viewControllerCount; - BOOL jsCatchingUp = - // --- previously not caught up ---- --------- now caught up ---------- - viewControllerCount != previousReactCount && currentReactCount == viewControllerCount; - BOOL jsMakingNoProgressButNeedsToCatchUp = - // --- previously not caught up ---- ------- still the same ----------- - viewControllerCount != previousReactCount && currentReactCount == previousReactCount; - BOOL jsMakingNoProgressAndDoesntNeedTo = - // --- previously caught up -------- ------- still caught up ---------- - viewControllerCount == previousReactCount && currentReactCount == previousReactCount; - -BOOL jsGettingtooSlow = - // --- previously not caught up -------- ------- no longer caught up ---------- - viewControllerCount < previousReactCount && currentReactCount < previousReactCount; - - BOOL reactPushOne = jsGettingAhead && currentReactCount == previousReactCount + 1; - BOOL reactPopN = jsGettingAhead && currentReactCount < previousReactCount; - - // We can actually recover from this situation, but it would be nice to know - // when this error happens. This simply means that JS hasn't caught up to a - // back navigation before progressing. It's likely a bug in the JS code that - // catches up/schedules navigations. - if (!(jsGettingAhead || - jsCatchingUp || - jsMakingNoProgressButNeedsToCatchUp || - jsMakingNoProgressAndDoesntNeedTo || - jsGettingtooSlow)) { - RCTLogError(@"JS has only made partial progress to catch up to UIKit"); - } - if (currentReactCount > self.reactSubviews.count) { - RCTLogError(@"Cannot adjust current top of stack beyond available views"); - } - - // Views before the previous React count must not have changed. Views greater than previousReactCount - // up to currentReactCount may have changed. - for (NSUInteger i = 0; i < MIN(self.reactSubviews.count, MIN(_previousViews.count, previousReactCount)); i++) { - if (self.reactSubviews[i] != _previousViews[i]) { - RCTLogError(@"current view should equal previous view"); - } - } - if (currentReactCount < 1) { - RCTLogError(@"should be at least one current view"); - } - if (jsGettingAhead) { - if (reactPushOne) { - UIView *lastView = self.reactSubviews.lastObject; - RCTWrapperViewController *vc = [[RCTWrapperViewController alloc] initWithNavItem:(RCTNavItem *)lastView]; - vc.navigationListener = self; - _numberOfViewControllerMovesToIgnore = 1; - [_navigationController pushViewController:vc animated:(currentReactCount > 1)]; - } else if (reactPopN) { - UIViewController *viewControllerToPopTo = _navigationController.viewControllers[(currentReactCount - 1)]; - _numberOfViewControllerMovesToIgnore = viewControllerCount - currentReactCount; - [_navigationController popToViewController:viewControllerToPopTo animated:YES]; - } else { - RCTLogError(@"Pushing or popping more than one view at a time from JS"); - } - } else if (jsCatchingUp) { - [self freeLock]; // Nothing to push/pop - } else { - // Else, JS making no progress, could have been unrelated to anything nav. - return; - } - - // Only make a copy of the subviews whose validity we expect to be able to check (in the loop, above), - // otherwise we would unnecessarily retain a reference to view(s) no longer on the React navigation stack: - NSUInteger expectedCount = MIN(currentReactCount, self.reactSubviews.count); - _previousViews = [[self.reactSubviews subarrayWithRange: NSMakeRange(0, expectedCount)] copy]; - _previousRequestedTopOfStack = _requestedTopOfStack; -} - -// TODO: This will likely fail when performing multiple pushes/pops. We must -// free the lock only after the *last* push/pop. -- (void)wrapperViewController:(RCTWrapperViewController *)wrapperViewController -didMoveToNavigationController:(UINavigationController *)navigationController -{ - if (self.superview == nil) { - // If superview is nil, then a JS reload (Cmd+R) happened - // while a push/pop is in progress. - return; - } - - RCTAssert( - (navigationController == nil || [_navigationController.viewControllers containsObject:wrapperViewController]), - @"if navigation controller is not nil, it should contain the wrapper view controller" - ); - RCTAssert(_navigationController.navigationLock == RCTNavigationLockJavaScript || - _numberOfViewControllerMovesToIgnore == 0, - @"If JS doesn't have the lock there should never be any pending transitions"); - /** - * When JS has the lock we want to keep track of when the request completes - * the pending transition count hitting 0 signifies this, and should always - * remain at 0 when JS does not have the lock - */ - if (_numberOfViewControllerMovesToIgnore > 0) { - _numberOfViewControllerMovesToIgnore -= 1; - } - if (_numberOfViewControllerMovesToIgnore == 0) { - [self handleTopOfStackChanged]; - [self freeLock]; - } -} - -@end diff --git a/React/Views/RCTNavigatorManager.h b/React/Views/RCTNavigatorManager.h deleted file mode 100644 index 58c40c94e..000000000 --- a/React/Views/RCTNavigatorManager.h +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import - -@interface RCTNavigatorManager : RCTViewManager - -@end diff --git a/React/Views/RCTNavigatorManager.m b/React/Views/RCTNavigatorManager.m deleted file mode 100644 index 99a03cbb3..000000000 --- a/React/Views/RCTNavigatorManager.m +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import "RCTNavigatorManager.h" - -#import "RCTBridge.h" -#import "RCTConvert.h" -#import "RCTNavigator.h" -#import "RCTUIManager.h" -#import "RCTUIManagerObserverCoordinator.h" -#import "UIView+React.h" - -@interface RCTNavigatorManager () - -@end - -@implementation RCTNavigatorManager -{ - // The main thread only. - NSHashTable *_viewRegistry; -} - -- (void)setBridge:(RCTBridge *)bridge -{ - [super setBridge:bridge]; - - [self.bridge.uiManager.observerCoordinator addObserver:self]; -} - -- (void)invalidate -{ - [self.bridge.uiManager.observerCoordinator removeObserver:self]; -} - -RCT_EXPORT_MODULE() - -- (UIView *)view -{ - if (!_viewRegistry) { - _viewRegistry = [NSHashTable hashTableWithOptions:NSPointerFunctionsWeakMemory]; - } - - RCTNavigator *view = [[RCTNavigator alloc] initWithBridge:self.bridge]; - [_viewRegistry addObject:view]; - return view; -} - -RCT_EXPORT_VIEW_PROPERTY(requestedTopOfStack, NSInteger) -RCT_EXPORT_VIEW_PROPERTY(onNavigationProgress, RCTDirectEventBlock) -RCT_EXPORT_VIEW_PROPERTY(onNavigationComplete, RCTBubblingEventBlock) -RCT_EXPORT_VIEW_PROPERTY(interactivePopGestureEnabled, BOOL) - -RCT_EXPORT_METHOD(requestSchedulingJavaScriptNavigation:(nonnull NSNumber *)reactTag - callback:(RCTResponseSenderBlock)callback) -{ - [self.bridge.uiManager addUIBlock: - ^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry){ - RCTNavigator *navigator = viewRegistry[reactTag]; - if ([navigator isKindOfClass:[RCTNavigator class]]) { - BOOL wasAcquired = [navigator requestSchedulingJavaScriptNavigation]; - callback(@[@(wasAcquired)]); - } else { - RCTLogError(@"Cannot set lock: %@ (tag #%@) is not an RCTNavigator", navigator, reactTag); - } - }]; -} - -#pragma mark - RCTUIManagerObserver - -- (void)uiManagerDidPerformMounting:(__unused RCTUIManager *)manager -{ - RCTExecuteOnMainQueue(^{ - for (RCTNavigator *view in self->_viewRegistry) { - [view uiManagerDidPerformMounting]; - } - }); -} - -@end diff --git a/React/Views/RCTWrapperViewController.h b/React/Views/RCTWrapperViewController.h index 7baa0c08c..675dc0a04 100644 --- a/React/Views/RCTWrapperViewController.h +++ b/React/Views/RCTWrapperViewController.h @@ -7,22 +7,10 @@ #import -@class RCTNavItem; @class RCTWrapperViewController; -@protocol RCTWrapperViewControllerNavigationListener - -- (void)wrapperViewController:(RCTWrapperViewController *)wrapperViewController -didMoveToNavigationController:(UINavigationController *)navigationController; - -@end - @interface RCTWrapperViewController : UIViewController - (instancetype)initWithContentView:(UIView *)contentView NS_DESIGNATED_INITIALIZER; -- (instancetype)initWithNavItem:(RCTNavItem *)navItem; - -@property (nonatomic, weak) id navigationListener; -@property (nonatomic, strong) RCTNavItem *navItem; @end diff --git a/React/Views/RCTWrapperViewController.m b/React/Views/RCTWrapperViewController.m index 30103019e..63df725fb 100644 --- a/React/Views/RCTWrapperViewController.m +++ b/React/Views/RCTWrapperViewController.m @@ -10,7 +10,6 @@ #import #import "RCTEventDispatcher.h" -#import "RCTNavItem.h" #import "RCTUtils.h" #import "UIView+React.h" #import "RCTAutoInsetsProtocol.h" @@ -38,14 +37,6 @@ return self; } -- (instancetype)initWithNavItem:(RCTNavItem *)navItem -{ - if ((self = [self initWithContentView:navItem])) { - _navItem = navItem; - } - return self; -} - RCT_NOT_IMPLEMENTED(- (instancetype)initWithNibName:(NSString *)nn bundle:(NSBundle *)nb) RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) @@ -83,55 +74,6 @@ static BOOL RCTFindScrollViewAndRefreshContentInsetInView(UIView *view) } } -static UIView *RCTFindNavBarShadowViewInView(UIView *view) -{ - if ([view isKindOfClass:[UIImageView class]] && view.bounds.size.height <= 1) { - return view; - } - for (UIView *subview in view.subviews) { - UIView *shadowView = RCTFindNavBarShadowViewInView(subview); - if (shadowView) { - return shadowView; - } - } - return nil; -} - -- (void)viewWillAppear:(BOOL)animated -{ - [super viewWillAppear:animated]; - - // TODO: find a way to make this less-tightly coupled to navigation controller - if ([self.parentViewController isKindOfClass:[UINavigationController class]]) - { - [self.navigationController - setNavigationBarHidden:_navItem.navigationBarHidden - animated:animated]; - - UINavigationBar *bar = self.navigationController.navigationBar; - bar.barTintColor = _navItem.barTintColor; - bar.tintColor = _navItem.tintColor; - bar.translucent = _navItem.translucent; -#if !TARGET_OS_TV - bar.barStyle = _navItem.barStyle; -#endif - bar.titleTextAttributes = _navItem.titleTextColor ? @{ - NSForegroundColorAttributeName: _navItem.titleTextColor - } : nil; - - RCTFindNavBarShadowViewInView(bar).hidden = _navItem.shadowHidden; - - UINavigationItem *item = self.navigationItem; - item.title = _navItem.title; - item.titleView = _navItem.titleImageView; -#if !TARGET_OS_TV - item.backBarButtonItem = _navItem.backButtonItem; -#endif //TARGET_OS_TV - item.leftBarButtonItem = _navItem.leftButtonItem; - item.rightBarButtonItem = _navItem.rightButtonItem; - } -} - - (void)loadView { // Add a wrapper so that the wrapper view managed by the @@ -142,16 +84,4 @@ static UIView *RCTFindNavBarShadowViewInView(UIView *view) self.view = _wrapperView; } -- (void)didMoveToParentViewController:(UIViewController *)parent -{ - // There's no clear setter for navigation controllers, but did move to parent - // view controller provides the desired effect. This is called after a pop - // finishes, be it a swipe to go back or a standard tap on the back button - [super didMoveToParentViewController:parent]; - if (parent == nil || [parent isKindOfClass:[UINavigationController class]]) { - [self.navigationListener wrapperViewController:self - didMoveToNavigationController:(UINavigationController *)parent]; - } -} - @end