diff --git a/Examples/UIExplorer/NavigatorIOSExample.js b/Examples/UIExplorer/NavigatorIOSExample.js index 56bb6ab784a646..b448604b5d23b4 100644 --- a/Examples/UIExplorer/NavigatorIOSExample.js +++ b/Examples/UIExplorer/NavigatorIOSExample.js @@ -13,6 +13,7 @@ var React = require('react-native'); var ViewExample = require('./ViewExample'); var { + AlertIOS, PixelRatio, ScrollView, StyleSheet, @@ -86,6 +87,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 06c353592d716c..50f0fe244bdce0 100644 --- a/Libraries/Components/Navigation/NavigatorIOS.ios.js +++ b/Libraries/Components/Navigation/NavigatorIOS.ios.js @@ -11,6 +11,7 @@ 'use strict'; var EventEmitter = require('EventEmitter'); +var Image = require('Image'); var React = require('React'); var ReactIOSViewAttributes = require('ReactIOSViewAttributes'); var RCTNavigatorManager = require('NativeModules').NavigatorManager; @@ -46,9 +47,14 @@ 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, tintColor: true, + backButtonImageName: true, backButtonTitle: true, titleTextColor: true, style: true, @@ -187,6 +193,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 @@ -194,6 +207,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 */ @@ -508,7 +541,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} tintColor={this.props.tintColor}> @@ -522,6 +560,10 @@ var NavigatorIOS = React.createClass({ ); }, + _imageNameFromSource: function(source) { + return source ? source.uri : undefined; + }, + renderNavigationStackItems: function() { var shouldRecurseToNavigator = this.state.makingNavigatorRequest || diff --git a/ReactKit/Modules/RCTUIManager.m b/ReactKit/Modules/RCTUIManager.m index 73c6929a7117c6..bb0b4d3806bbae 100644 --- a/ReactKit/Modules/RCTUIManager.m +++ b/ReactKit/Modules/RCTUIManager.m @@ -1130,6 +1130,12 @@ - (NSDictionary *)customBubblingEventTypes @"captured": @"onNavigationCompleteCapture" } }, + @"topNavLeftButtonTap": @{ + @"phasedRegistrationNames": @{ + @"bubbled": @"onNavLeftButtonTap", + @"captured": @"onNavLefttButtonTapCapture" + } + }, @"topNavRightButtonTap": @{ @"phasedRegistrationNames": @{ @"bubbled": @"onNavRightButtonTap", diff --git a/ReactKit/Views/RCTNavItem.h b/ReactKit/Views/RCTNavItem.h index 6db429db27495d..5abd9ed3ded1ab 100644 --- a/ReactKit/Views/RCTNavItem.h +++ b/ReactKit/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, copy) UIColor *tintColor; @property (nonatomic, copy) UIColor *barTintColor; diff --git a/ReactKit/Views/RCTNavItemManager.m b/ReactKit/Views/RCTNavItemManager.m index 1c574cbd18beee..2ad64efbd4f327 100644 --- a/ReactKit/Views/RCTNavItemManager.m +++ b/ReactKit/Views/RCTNavItemManager.m @@ -20,11 +20,27 @@ - (UIView *)view } RCT_EXPORT_VIEW_PROPERTY(title) +RCT_EXPORT_VIEW_PROPERTY(leftButtonTitle); RCT_EXPORT_VIEW_PROPERTY(rightButtonTitle); RCT_EXPORT_VIEW_PROPERTY(backButtonTitle); RCT_EXPORT_VIEW_PROPERTY(tintColor); RCT_EXPORT_VIEW_PROPERTY(barTintColor); RCT_EXPORT_VIEW_PROPERTY(titleTextColor); +- (void)set_leftButtonImageName:(id)json forView:(RCTNavItem *)view withDefaultView:(RCTNavItem *)defaultView +{ + view.leftButtonImage = json ? [RCTConvert UIImage:json] : defaultView.leftButtonImage; +} + +- (void)set_rightButtonImageName:(id)json forView:(RCTNavItem *)view withDefaultView:(RCTNavItem *)defaultView +{ + view.rightButtonImage = json ? [RCTConvert UIImage:json] : defaultView.rightButtonImage; +} + +- (void)set_backButtonImageName:(id)json forView:(RCTNavItem *)view withDefaultView:(RCTNavItem *)defaultView +{ + view.backButtonImage = json ? [RCTConvert UIImage:json] : defaultView.backButtonImage; +} + @end diff --git a/ReactKit/Views/RCTWrapperViewController.m b/ReactKit/Views/RCTWrapperViewController.m index e9b9d5c7798363..e010678a2f1f15 100644 --- a/ReactKit/Views/RCTWrapperViewController.m +++ b/ReactKit/Views/RCTWrapperViewController.m @@ -86,17 +86,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]; @@ -114,6 +140,12 @@ - (void)loadView self.view = _wrapperView; } +- (void)handleNavLeftButtonTapped +{ + [_eventDispatcher sendInputEventWithName:@"topNavLeftButtonTap" + body:@{@"target":_navItem.reactTag}]; +} + - (void)handleNavRightButtonTapped { [_eventDispatcher sendInputEventWithName:@"topNavRightButtonTap"