diff --git a/Examples/UIExplorer/ImageMocks.js b/Examples/UIExplorer/ImageMocks.js index b888acbf74ca41..3f1883fa65c5bb 100644 --- a/Examples/UIExplorer/ImageMocks.js +++ b/Examples/UIExplorer/ImageMocks.js @@ -39,3 +39,8 @@ declare module 'image!uie_thumb_selected' { declare var uri: string; declare var isStatic: boolean; } + +declare module 'image!NavBarButtonPlus' { + declare var uri: string; + declare var isStatic: boolean; +} diff --git a/Examples/UIExplorer/NavigatorIOSExample.js b/Examples/UIExplorer/NavigatorIOSExample.js index eee731bd53834a..f98c2996efaf93 100644 --- a/Examples/UIExplorer/NavigatorIOSExample.js +++ b/Examples/UIExplorer/NavigatorIOSExample.js @@ -19,6 +19,7 @@ var React = require('react-native'); var ViewExample = require('./ViewExample'); var createExamplePage = require('./createExamplePage'); var { + AlertIOS, PixelRatio, ScrollView, StyleSheet, @@ -92,6 +93,30 @@ var NavigatorIOSExample = React.createClass({ } }); })} + {this._renderRow('Custom Left & Right Icons', () => { + this.props.navigator.push({ + title: NavigatorIOSExample.title, + component: EmptyPage, + leftButtonTitle: 'Custom Left', + onLeftButtonPress: () => this.props.navigator.pop(), + rightButtonImageSource: require('image!NavBarButtonPlus'), + 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(); })} diff --git a/Examples/UIExplorer/UIExplorer/Images.xcassets/NavBarButtonPlus.imageset/Contents.json b/Examples/UIExplorer/UIExplorer/Images.xcassets/NavBarButtonPlus.imageset/Contents.json new file mode 100644 index 00000000000000..8af814b687aebd --- /dev/null +++ b/Examples/UIExplorer/UIExplorer/Images.xcassets/NavBarButtonPlus.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x", + "filename" : "NavBarButtonPlus@3x.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Examples/UIExplorer/UIExplorer/Images.xcassets/NavBarButtonPlus.imageset/NavBarButtonPlus@3x.png b/Examples/UIExplorer/UIExplorer/Images.xcassets/NavBarButtonPlus.imageset/NavBarButtonPlus@3x.png new file mode 100644 index 00000000000000..551cea0d08693d Binary files /dev/null and b/Examples/UIExplorer/UIExplorer/Images.xcassets/NavBarButtonPlus.imageset/NavBarButtonPlus@3x.png differ diff --git a/Libraries/Components/Navigation/NavigatorIOS.ios.js b/Libraries/Components/Navigation/NavigatorIOS.ios.js index fd90847f05c61f..27c914789511c4 100644 --- a/Libraries/Components/Navigation/NavigatorIOS.ios.js +++ b/Libraries/Components/Navigation/NavigatorIOS.ios.js @@ -12,6 +12,7 @@ 'use strict'; var EventEmitter = require('EventEmitter'); +var Image = require('Image'); var React = require('React'); var ReactIOSViewAttributes = require('ReactIOSViewAttributes'); var RCTNavigatorManager = require('NativeModules').NavigatorManager; @@ -47,11 +48,16 @@ var RCTNavigatorItem = createReactIOSNativeComponentClass({ // NavigatorIOS does not use them all, because some are problematic title: true, barTintColor: true, + leftButtonImageName: true, + leftButtonTitle: true, + onNavLeftButtonTap: true, + rightButtonImageName: true, rightButtonTitle: true, onNavRightButtonTap: true, + backButtonImageName: true, + backButtonTitle: true, tintColor: true, navigationBarHidden: true, - backButtonTitle: true, titleTextColor: true, style: true, }, @@ -79,7 +85,12 @@ type Route = { title: string; passProps: Object; backButtonTitle: string; + backButtonImageSource: Object; + leftButtonTitle: string; + leftButtonImageSource: Object; + onLeftButtonPress: Function; rightButtonTitle: string; + rightButtonImageSource: Object; onRightButtonPress: Function; wrapperStyle: any; }; @@ -212,6 +223,13 @@ var NavigatorIOS = React.createClass({ */ passProps: PropTypes.object, + /** + * If set, the left header button image will appear with this source. Note + * that this doesn't apply for the header of the current view, but the + * ones of the views that are pushed afterward. + */ + backButtonImageSource: Image.propTypes.source, + /** * If set, the left header button will appear with this name. Note that * this doesn't apply for the header of the current view, but the ones @@ -219,6 +237,26 @@ var NavigatorIOS = React.createClass({ */ backButtonTitle: PropTypes.string, + /** + * If set, the left header button image will appear with this source + */ + leftButtonImageSource: Image.propTypes.source, + + /** + * If set, the left header button will appear with this name + */ + leftButtonTitle: PropTypes.string, + + /** + * Called when the left header button is pressed + */ + onLeftButtonPress: PropTypes.func, + + /** + * If set, the right header button image will appear with this source + */ + rightButtonImageSource: Image.propTypes.source, + /** * If set, the right header button will appear with this name */ @@ -550,7 +588,12 @@ var NavigatorIOS = React.createClass({ this.props.itemWrapperStyle, route.wrapperStyle ]} + backButtonImageName={this._imageNameFromSource(route.backButtonImageSource)} backButtonTitle={route.backButtonTitle} + leftButtonImageName={this._imageNameFromSource(route.leftButtonImageSource)} + leftButtonTitle={route.leftButtonTitle} + onNavLeftButtonTap={route.onLeftButtonPress} + rightButtonImageName={this._imageNameFromSource(route.rightButtonImageSource)} rightButtonTitle={route.rightButtonTitle} onNavRightButtonTap={route.onRightButtonPress} navigationBarHidden={this.props.navigationBarHidden} @@ -565,6 +608,10 @@ var NavigatorIOS = React.createClass({ ); }, + _imageNameFromSource: function(source: ?Object) { + return source ? source.uri : undefined; + }, + renderNavigationStackItems: function() { var shouldRecurseToNavigator = this.state.makingNavigatorRequest || diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index b6a350dcd4964e..0c66d6e5f8071f 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -1165,6 +1165,12 @@ - (NSDictionary *)customBubblingEventTypes @"captured": @"onNavigationCompleteCapture" } }, + @"topNavLeftButtonTap": @{ + @"phasedRegistrationNames": @{ + @"bubbled": @"onNavLeftButtonTap", + @"captured": @"onNavLefttButtonTapCapture" + } + }, @"topNavRightButtonTap": @{ @"phasedRegistrationNames": @{ @"bubbled": @"onNavRightButtonTap", diff --git a/React/Views/RCTNavItem.h b/React/Views/RCTNavItem.h index 5ae874522e8a5d..127da77e94d6e1 100644 --- a/React/Views/RCTNavItem.h +++ b/React/Views/RCTNavItem.h @@ -12,7 +12,11 @@ @interface RCTNavItem : UIView @property (nonatomic, copy) NSString *title; +@property (nonatomic, copy) UIImage *leftButtonImage; +@property (nonatomic, copy) NSString *leftButtonTitle; +@property (nonatomic, copy) UIImage *rightButtonImage; @property (nonatomic, copy) NSString *rightButtonTitle; +@property (nonatomic, copy) UIImage *backButtonImage; @property (nonatomic, copy) NSString *backButtonTitle; @property (nonatomic, assign) BOOL navigationBarHidden; @property (nonatomic, copy) UIColor *tintColor; diff --git a/React/Views/RCTNavItemManager.m b/React/Views/RCTNavItemManager.m index fc601632f4c4e4..b21ad70b56d69d 100644 --- a/React/Views/RCTNavItemManager.m +++ b/React/Views/RCTNavItemManager.m @@ -22,11 +22,24 @@ - (UIView *)view } RCT_EXPORT_VIEW_PROPERTY(title, NSString) +RCT_EXPORT_VIEW_PROPERTY(leftButtonTitle, NSString); RCT_EXPORT_VIEW_PROPERTY(rightButtonTitle, NSString); RCT_EXPORT_VIEW_PROPERTY(backButtonTitle, NSString); RCT_EXPORT_VIEW_PROPERTY(navigationBarHidden, BOOL); RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor); RCT_EXPORT_VIEW_PROPERTY(barTintColor, UIColor); RCT_EXPORT_VIEW_PROPERTY(titleTextColor, UIColor); +RCT_CUSTOM_VIEW_PROPERTY(leftButtonImageName, UIImage, RCTNavItem) +{ + view.leftButtonImage = json ? [RCTConvert UIImage:json] : defaultView.leftButtonImage; +} +RCT_CUSTOM_VIEW_PROPERTY(rightButtonImageName, UIImage, RCTNavItem) +{ + view.rightButtonImage = json ? [RCTConvert UIImage:json] : defaultView.rightButtonImage; +} +RCT_CUSTOM_VIEW_PROPERTY(backButtonImageName, UIImage, RCTNavItem) +{ + view.backButtonImage = json ? [RCTConvert UIImage:json] : defaultView.backButtonImage; +} @end diff --git a/React/Views/RCTWrapperViewController.m b/React/Views/RCTWrapperViewController.m index f9f35163448f78..4c5acd812c7d70 100644 --- a/React/Views/RCTWrapperViewController.m +++ b/React/Views/RCTWrapperViewController.m @@ -89,17 +89,43 @@ - (void)viewWillAppear:(BOOL)animated [bar setTitleTextAttributes:@{NSForegroundColorAttributeName : _navItem.titleTextColor}]; } - if (_navItem.rightButtonTitle.length > 0) { + if (_navItem.leftButtonImage) { + self.navigationItem.leftBarButtonItem = + [[UIBarButtonItem alloc] initWithImage:_navItem.leftButtonImage + style:UIBarButtonItemStylePlain + target:self + action:@selector(handleNavLeftButtonTapped)]; + } else if (_navItem.leftButtonTitle.length > 0) { + self.navigationItem.leftBarButtonItem = + [[UIBarButtonItem alloc] initWithTitle:_navItem.leftButtonTitle + style:UIBarButtonItemStylePlain + target:self + action:@selector(handleNavLeftButtonTapped)]; + } + + if (_navItem.rightButtonImage) { self.navigationItem.rightBarButtonItem = - [[UIBarButtonItem alloc] initWithTitle:_navItem.rightButtonTitle - style:UIBarButtonItemStyleDone - target:self - action:@selector(handleNavRightButtonTapped)]; + [[UIBarButtonItem alloc] initWithImage:_navItem.rightButtonImage + style:UIBarButtonItemStylePlain + target:self + action:@selector(handleNavRightButtonTapped)]; + } else if (_navItem.rightButtonTitle.length > 0) { + self.navigationItem.rightBarButtonItem = + [[UIBarButtonItem alloc] initWithTitle:_navItem.rightButtonTitle + style:UIBarButtonItemStyleDone + target:self + action:@selector(handleNavRightButtonTapped)]; } - if (_navItem.backButtonTitle.length > 0) { + if (_navItem.backButtonImage) { + self.navigationItem.backBarButtonItem = + [[UIBarButtonItem alloc] initWithImage:_navItem.backButtonImage + style:UIBarButtonItemStylePlain + target:nil + action:nil]; + } else if (_navItem.backButtonTitle.length > 0) { self.navigationItem.backBarButtonItem = - [[UIBarButtonItem alloc] initWithTitle:_navItem.backButtonTitle + [[UIBarButtonItem alloc] initWithTitle:_navItem.backButtonTitle style:UIBarButtonItemStylePlain target:nil action:nil]; @@ -117,6 +143,12 @@ - (void)loadView self.view = _wrapperView; } +- (void)handleNavLeftButtonTapped +{ + [_eventDispatcher sendInputEventWithName:@"topNavLeftButtonTap" + body:@{@"target":_navItem.reactTag}]; +} + - (void)handleNavRightButtonTapped { [_eventDispatcher sendInputEventWithName:@"topNavRightButtonTap"