[Nav] Add support for bar button icons and left buttons

Summary:
NavigatorIOS supports four new properties:

  - **rightButtonImageSource:** The source of an image to display in the top right. This must be a static image since UINavigationController only supports UIImages. Adding support for UIImageViews (or arbitrary views) is more complicated because custom views do not fade on touch and do not have hit slop the same way that UIImage buttons do. Usage: `rightButtonImageSource: ix('ImageName')`
  - **backButtonImageSource:** Use a custom image for the back button. This does not replace the back caret (`<`) but instead replaces the text next to it.
  - **leftButtonTitle**: Text for the left nav button, which supersedes the previous nav item's back button when specified. The main use case for this is your initial screen/UIVC which has nothing to go back to (since it is the first VC on the stack) but need to display a left button. This does hide the back button if there would have been one otherwise.
  - **leftButtonImageSource:** Image source for the left button, super
Closes https://github.com/facebook/react-native/pull/263
Github Author: James Ide <ide@jameside.com>

Test Plan: Imported from GitHub, without a `Test Plan:` line.
This commit is contained in:
James Ide 2015-05-07 07:16:59 -07:00
Родитель b8bf4d0957
Коммит b97ce93cea
10 изменённых файлов: 247 добавлений и 33 удалений

Просмотреть файл

@ -39,3 +39,8 @@ declare module 'image!uie_thumb_selected' {
declare var uri: string; declare var uri: string;
declare var isStatic: boolean; declare var isStatic: boolean;
} }
declare module 'image!NavBarButtonPlus' {
declare var uri: string;
declare var isStatic: boolean;
}

Просмотреть файл

@ -19,6 +19,7 @@ var React = require('react-native');
var ViewExample = require('./ViewExample'); var ViewExample = require('./ViewExample');
var createExamplePage = require('./createExamplePage'); var createExamplePage = require('./createExamplePage');
var { var {
AlertIOS,
PixelRatio, PixelRatio,
ScrollView, ScrollView,
StyleSheet, 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(),
rightButtonIcon: 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._renderRow('Pop', () => {
this.props.navigator.pop(); this.props.navigator.pop();
})} })}

Просмотреть файл

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "NavBarButtonPlus@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Двоичные данные
Examples/UIExplorer/UIExplorer/Images.xcassets/NavBarButtonPlus.imageset/NavBarButtonPlus@3x.png поставляемый Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 166 B

Просмотреть файл

@ -12,6 +12,7 @@
'use strict'; 'use strict';
var EventEmitter = require('EventEmitter'); var EventEmitter = require('EventEmitter');
var Image = require('Image');
var React = require('React'); var React = require('React');
var ReactIOSViewAttributes = require('ReactIOSViewAttributes'); var ReactIOSViewAttributes = require('ReactIOSViewAttributes');
var RCTNavigatorManager = require('NativeModules').NavigatorManager; var RCTNavigatorManager = require('NativeModules').NavigatorManager;
@ -47,11 +48,16 @@ var RCTNavigatorItem = createReactIOSNativeComponentClass({
// NavigatorIOS does not use them all, because some are problematic // NavigatorIOS does not use them all, because some are problematic
title: true, title: true,
barTintColor: true, barTintColor: true,
leftButtonIcon: true,
leftButtonTitle: true,
onNavLeftButtonTap: true,
rightButtonIcon: true,
rightButtonTitle: true, rightButtonTitle: true,
onNavRightButtonTap: true, onNavRightButtonTap: true,
backButtonIcon: true,
backButtonTitle: true,
tintColor: true, tintColor: true,
navigationBarHidden: true, navigationBarHidden: true,
backButtonTitle: true,
titleTextColor: true, titleTextColor: true,
style: true, style: true,
}, },
@ -79,7 +85,12 @@ type Route = {
title: string; title: string;
passProps: Object; passProps: Object;
backButtonTitle: string; backButtonTitle: string;
backButtonIcon: Object;
leftButtonTitle: string;
leftButtonIcon: Object;
onLeftButtonPress: Function;
rightButtonTitle: string; rightButtonTitle: string;
rightButtonIcon: Object;
onRightButtonPress: Function; onRightButtonPress: Function;
wrapperStyle: any; wrapperStyle: any;
}; };
@ -212,6 +223,13 @@ var NavigatorIOS = React.createClass({
*/ */
passProps: PropTypes.object, 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.
*/
backButtonIcon: Image.propTypes.source,
/** /**
* If set, the left header button will appear with this name. Note that * 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 * 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, backButtonTitle: PropTypes.string,
/**
* If set, the left header button image will appear with this source
*/
leftButtonIcon: 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
*/
rightButtonIcon: Image.propTypes.source,
/** /**
* If set, the right header button will appear with this name * If set, the right header button will appear with this name
*/ */
@ -560,7 +598,12 @@ var NavigatorIOS = React.createClass({
this.props.itemWrapperStyle, this.props.itemWrapperStyle,
route.wrapperStyle route.wrapperStyle
]} ]}
backButtonIcon={this._imageNameFromSource(route.backButtonIcon)}
backButtonTitle={route.backButtonTitle} backButtonTitle={route.backButtonTitle}
leftButtonIcon={this._imageNameFromSource(route.leftButtonIcon)}
leftButtonTitle={route.leftButtonTitle}
onNavLeftButtonTap={route.onLeftButtonPress}
rightButtonIcon={this._imageNameFromSource(route.rightButtonIcon)}
rightButtonTitle={route.rightButtonTitle} rightButtonTitle={route.rightButtonTitle}
onNavRightButtonTap={route.onRightButtonPress} onNavRightButtonTap={route.onRightButtonPress}
navigationBarHidden={this.props.navigationBarHidden} navigationBarHidden={this.props.navigationBarHidden}
@ -577,6 +620,10 @@ var NavigatorIOS = React.createClass({
); );
}, },
_imageNameFromSource: function(source: ?Object) {
return source ? source.uri : undefined;
},
renderNavigationStackItems: function() { renderNavigationStackItems: function() {
var shouldRecurseToNavigator = var shouldRecurseToNavigator =
this.state.makingNavigatorRequest || this.state.makingNavigatorRequest ||

Просмотреть файл

@ -1159,6 +1159,12 @@ RCT_EXPORT_METHOD(clearJSResponder)
@"captured": @"onNavigationCompleteCapture" @"captured": @"onNavigationCompleteCapture"
} }
}, },
@"topNavLeftButtonTap": @{
@"phasedRegistrationNames": @{
@"bubbled": @"onNavLeftButtonTap",
@"captured": @"onNavLefttButtonTapCapture"
}
},
@"topNavRightButtonTap": @{ @"topNavRightButtonTap": @{
@"phasedRegistrationNames": @{ @"phasedRegistrationNames": @{
@"bubbled": @"onNavRightButtonTap", @"bubbled": @"onNavRightButtonTap",

Просмотреть файл

@ -12,11 +12,19 @@
@interface RCTNavItem : UIView @interface RCTNavItem : UIView
@property (nonatomic, copy) NSString *title; @property (nonatomic, copy) NSString *title;
@property (nonatomic, strong) UIImage *leftButtonIcon;
@property (nonatomic, copy) NSString *leftButtonTitle;
@property (nonatomic, strong) UIImage *rightButtonIcon;
@property (nonatomic, copy) NSString *rightButtonTitle; @property (nonatomic, copy) NSString *rightButtonTitle;
@property (nonatomic, strong) UIImage *backButtonIcon;
@property (nonatomic, copy) NSString *backButtonTitle; @property (nonatomic, copy) NSString *backButtonTitle;
@property (nonatomic, assign) BOOL navigationBarHidden; @property (nonatomic, assign) BOOL navigationBarHidden;
@property (nonatomic, copy) UIColor *tintColor; @property (nonatomic, strong) UIColor *tintColor;
@property (nonatomic, copy) UIColor *barTintColor; @property (nonatomic, strong) UIColor *barTintColor;
@property (nonatomic, copy) UIColor *titleTextColor; @property (nonatomic, strong) UIColor *titleTextColor;
@property (nonatomic, readonly) UIBarButtonItem *backButtonItem;
@property (nonatomic, readonly) UIBarButtonItem *leftButtonItem;
@property (nonatomic, readonly) UIBarButtonItem *rightButtonItem;
@end @end

Просмотреть файл

@ -11,5 +11,104 @@
@implementation RCTNavItem @implementation RCTNavItem
@end @synthesize backButtonItem = _backButtonItem;
@synthesize leftButtonItem = _leftButtonItem;
@synthesize rightButtonItem = _rightButtonItem;
- (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;
_leftButtonIcon = nil;
}
- (UIBarButtonItem *)leftButtonItem
{
if (!_leftButtonItem) {
if (_leftButtonIcon) {
_leftButtonItem = [[UIBarButtonItem alloc] initWithImage:_leftButtonIcon
style:UIBarButtonItemStylePlain
target:nil
action:nil];
} else if (_leftButtonTitle.length) {
_leftButtonItem = [[UIBarButtonItem alloc] initWithTitle:_leftButtonTitle
style:UIBarButtonItemStylePlain
target:nil
action:nil];
} else {
_leftButtonItem = nil;
}
}
return _leftButtonItem;
}
- (void)setRightButtonTitle:(NSString *)rightButtonTitle
{
_rightButtonTitle = rightButtonTitle;
_rightButtonItem = nil;
}
- (void)setRightButtonIcon:(UIImage *)rightButtonIcon
{
_rightButtonIcon = rightButtonIcon;
_rightButtonItem = nil;
}
- (UIBarButtonItem *)rightButtonItem
{
if (!_rightButtonItem) {
if (_rightButtonIcon) {
_rightButtonItem = [[UIBarButtonItem alloc] initWithImage:_rightButtonIcon
style:UIBarButtonItemStylePlain
target:nil
action:nil];
} else if (_rightButtonTitle.length) {
_rightButtonItem = [[UIBarButtonItem alloc] initWithTitle:_rightButtonTitle
style:UIBarButtonItemStylePlain
target:nil
action:nil];
} else {
_rightButtonItem = nil;
}
}
return _rightButtonItem;
}
@end

Просмотреть файл

@ -21,12 +21,20 @@ RCT_EXPORT_MODULE()
return [[RCTNavItem alloc] init]; return [[RCTNavItem alloc] init];
} }
RCT_EXPORT_VIEW_PROPERTY(navigationBarHidden, BOOL)
RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor)
RCT_EXPORT_VIEW_PROPERTY(barTintColor, UIColor)
RCT_EXPORT_VIEW_PROPERTY(title, NSString) RCT_EXPORT_VIEW_PROPERTY(title, NSString)
RCT_EXPORT_VIEW_PROPERTY(rightButtonTitle, NSString); RCT_EXPORT_VIEW_PROPERTY(titleTextColor, UIColor)
RCT_EXPORT_VIEW_PROPERTY(backButtonTitle, NSString);
RCT_EXPORT_VIEW_PROPERTY(navigationBarHidden, BOOL); RCT_EXPORT_VIEW_PROPERTY(backButtonIcon, UIImage)
RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor); RCT_EXPORT_VIEW_PROPERTY(backButtonTitle, NSString)
RCT_EXPORT_VIEW_PROPERTY(barTintColor, UIColor);
RCT_EXPORT_VIEW_PROPERTY(titleTextColor, UIColor); RCT_EXPORT_VIEW_PROPERTY(leftButtonTitle, NSString)
RCT_EXPORT_VIEW_PROPERTY(leftButtonIcon, UIImage)
RCT_EXPORT_VIEW_PROPERTY(rightButtonIcon, UIImage)
RCT_EXPORT_VIEW_PROPERTY(rightButtonTitle, NSString)
@end @end

Просмотреть файл

@ -64,7 +64,6 @@
// TODO: find a way to make this less-tightly coupled to navigation controller // TODO: find a way to make this less-tightly coupled to navigation controller
if ([self.parentViewController isKindOfClass:[UINavigationController class]]) if ([self.parentViewController isKindOfClass:[UINavigationController class]])
{ {
[self.navigationController [self.navigationController
setNavigationBarHidden:_navItem.navigationBarHidden setNavigationBarHidden:_navItem.navigationBarHidden
animated:animated]; animated:animated];
@ -73,33 +72,23 @@
return; return;
} }
self.navigationItem.title = _navItem.title;
UINavigationBar *bar = self.navigationController.navigationBar; UINavigationBar *bar = self.navigationController.navigationBar;
if (_navItem.barTintColor) {
bar.barTintColor = _navItem.barTintColor; bar.barTintColor = _navItem.barTintColor;
}
if (_navItem.tintColor) {
bar.tintColor = _navItem.tintColor; bar.tintColor = _navItem.tintColor;
}
if (_navItem.titleTextColor) { if (_navItem.titleTextColor) {
[bar setTitleTextAttributes:@{NSForegroundColorAttributeName : _navItem.titleTextColor}]; [bar setTitleTextAttributes:@{NSForegroundColorAttributeName : _navItem.titleTextColor}];
} }
if (_navItem.rightButtonTitle.length > 0) { UINavigationItem *item = self.navigationItem;
self.navigationItem.rightBarButtonItem = item.title = _navItem.title;
[[UIBarButtonItem alloc] initWithTitle:_navItem.rightButtonTitle item.backBarButtonItem = _navItem.backButtonItem;
style:UIBarButtonItemStyleDone if ((item.leftBarButtonItem = _navItem.leftButtonItem)) {
target:self item.leftBarButtonItem.target = self;
action:@selector(handleNavRightButtonTapped)]; item.leftBarButtonItem.action = @selector(handleNavLeftButtonTapped);
} }
if ((item.rightBarButtonItem = _navItem.rightButtonItem)) {
if (_navItem.backButtonTitle.length > 0) { item.rightBarButtonItem.target = self;
self.navigationItem.backBarButtonItem = item.rightBarButtonItem.action = @selector(handleNavRightButtonTapped);
[[UIBarButtonItem alloc] initWithTitle:_navItem.backButtonTitle
style:UIBarButtonItemStylePlain
target:nil
action:nil];
} }
} }
} }
@ -114,6 +103,12 @@
self.view = _wrapperView; self.view = _wrapperView;
} }
- (void)handleNavLeftButtonTapped
{
[_eventDispatcher sendInputEventWithName:@"topNavLeftButtonTap"
body:@{@"target":_navItem.reactTag}];
}
- (void)handleNavRightButtonTapped - (void)handleNavRightButtonTapped
{ {
[_eventDispatcher sendInputEventWithName:@"topNavRightButtonTap" [_eventDispatcher sendInputEventWithName:@"topNavRightButtonTap"