From e2c35dddbac6ce305f1ed7b445e92fd392b42865 Mon Sep 17 00:00:00 2001 From: Emilio Rodriguez Date: Tue, 8 Dec 2015 07:44:56 -0800 Subject: [PATCH] Added support for styling the PickerIOS Summary: - PickerIOS accepts now a new prop: style - this prop modifies the native style of the RCTPicker allowing to modify the font size of the items (fontSize), color of the items (color, only 6 char HEX values for now) and alignment of the items (textAlign) Closes https://github.com/facebook/react-native/pull/4490 Reviewed By: svcscm Differential Revision: D2723190 Pulled By: nicklockwood fb-gh-sync-id: ab9188192f1d0d087787dfed8c128073bfaa3235 --- Examples/UIExplorer/PickerIOSExample.js | 54 +++++++++++++++++++------ Libraries/Picker/PickerIOS.ios.js | 16 ++++++-- React/Views/RCTPicker.h | 5 +++ React/Views/RCTPicker.m | 43 ++++++++++++++------ React/Views/RCTPickerManager.m | 18 +++++++++ 5 files changed, 107 insertions(+), 29 deletions(-) diff --git a/Examples/UIExplorer/PickerIOSExample.js b/Examples/UIExplorer/PickerIOSExample.js index b0cc5cb97..d38ef54e9 100644 --- a/Examples/UIExplorer/PickerIOSExample.js +++ b/Examples/UIExplorer/PickerIOSExample.js @@ -87,24 +87,21 @@ var PickerExample = React.createClass({ key={carMake} value={carMake} label={CAR_MAKES_AND_MODELS[carMake].name} - /> - ) - )} + /> + ))} Please choose a model of {make.name}: this.setState({modelIndex})}> - {CAR_MAKES_AND_MODELS[this.state.carMake].models.map( - (modelName, modelIndex) => ( - - )) - } + {CAR_MAKES_AND_MODELS[this.state.carMake].models.map((modelName, modelIndex) => ( + + ))} You selected: {selectionString} @@ -112,6 +109,33 @@ var PickerExample = React.createClass({ }, }); +var PickerStyleExample = React.createClass({ + getInitialState: function() { + return { + carMake: 'cadillac', + }; + }, + + render: function() { + var make = CAR_MAKES_AND_MODELS[this.state.carMake]; + var selectionString = make.name + ' ' + make.models[this.state.modelIndex]; + return ( + this.setState({carMake, modelIndex: 0})}> + {Object.keys(CAR_MAKES_AND_MODELS).map((carMake) => ( + + ))} + + ); + }, +}); + exports.displayName = (undefined: ?string); exports.title = ''; exports.description = 'Render lists of selectable options with UIPickerView.'; @@ -121,4 +145,10 @@ exports.examples = [ render: function(): ReactElement { return ; }, +}, +{ + title: ' with custom styling', + render: function(): ReactElement { + return ; + }, }]; diff --git a/Libraries/Picker/PickerIOS.ios.js b/Libraries/Picker/PickerIOS.ios.js index ffda1f57d..0aa82bb5f 100644 --- a/Libraries/Picker/PickerIOS.ios.js +++ b/Libraries/Picker/PickerIOS.ios.js @@ -17,8 +17,11 @@ var React = require('React'); var ReactChildren = require('ReactChildren'); var RCTPickerIOSConsts = require('NativeModules').UIManager.RCTPicker.Constants; var StyleSheet = require('StyleSheet'); +var StyleSheetPropType = require('StyleSheetPropType'); +var TextStylePropTypes = require('TextStylePropTypes'); var View = require('View'); +var itemStylePropType = StyleSheetPropType(TextStylePropTypes); var requireNativeComponent = require('requireNativeComponent'); var PickerIOS = React.createClass({ @@ -26,6 +29,7 @@ var PickerIOS = React.createClass({ propTypes: { ...View.propTypes, + itemStyle: itemStylePropType, onValueChange: React.PropTypes.func, selectedValue: React.PropTypes.any, // string or integer basically }, @@ -50,13 +54,13 @@ var PickerIOS = React.createClass({ }); return {selectedIndex, items}; }, - + render: function() { return ( this._picker = picker } - style={styles.pickerIOS} + ref={picker => this._picker = picker} + style={[styles.pickerIOS, this.props.itemStyle]} items={this.state.items} selectedIndex={this.state.selectedIndex} onChange={this._onChange} @@ -108,7 +112,11 @@ var styles = StyleSheet.create({ }, }); -var RCTPickerIOS = requireNativeComponent('RCTPicker', PickerIOS, { +var RCTPickerIOS = requireNativeComponent('RCTPicker', { + propTypes: { + style: itemStylePropType, + }, +}, { nativeOnly: { items: true, onChange: true, diff --git a/React/Views/RCTPicker.h b/React/Views/RCTPicker.h index 8fb39b5a5..0066d140f 100644 --- a/React/Views/RCTPicker.h +++ b/React/Views/RCTPicker.h @@ -15,6 +15,11 @@ @property (nonatomic, copy) NSArray *items; @property (nonatomic, assign) NSInteger selectedIndex; + +@property (nonatomic, strong) UIColor *color; +@property (nonatomic, strong) UIFont *font; +@property (nonatomic, assign) NSTextAlignment textAlign; + @property (nonatomic, copy) RCTBubblingEventBlock onChange; @end diff --git a/React/Views/RCTPicker.m b/React/Views/RCTPicker.m index f979c2116..b83a9667c 100644 --- a/React/Views/RCTPicker.m +++ b/React/Views/RCTPicker.m @@ -9,6 +9,7 @@ #import "RCTPicker.h" +#import "RCTConvert.h" #import "RCTUtils.h" @interface RCTPicker() @@ -19,7 +20,10 @@ - (instancetype)initWithFrame:(CGRect)frame { if ((self = [super initWithFrame:frame])) { + _color = [UIColor blackColor]; + _font = [UIFont systemFontOfSize:21]; // TODO: selected title default should be 23.5 _selectedIndex = NSNotFound; + _textAlign = NSTextAlignmentCenter; self.delegate = self; } return self; @@ -59,20 +63,33 @@ numberOfRowsInComponent:(__unused NSInteger)component #pragma mark - UIPickerViewDelegate methods -- (NSDictionary *)itemForRow:(NSInteger)row -{ - return _items[row]; -} - -- (id)valueForRow:(NSInteger)row -{ - return [self itemForRow:row][@"value"]; -} - - (NSString *)pickerView:(__unused UIPickerView *)pickerView - titleForRow:(NSInteger)row forComponent:(__unused NSInteger)component + titleForRow:(NSInteger)row + forComponent:(__unused NSInteger)component { - return [self itemForRow:row][@"label"]; + return [RCTConvert NSString:_items[row][@"label"]]; +} + +- (UIView *)pickerView:(UIPickerView *)pickerView + viewForRow:(NSInteger)row + forComponent:(NSInteger)component + reusingView:(UILabel *)label +{ + if (!label) { + label = [[UILabel alloc] initWithFrame:(CGRect){ + CGPointZero, + { + [pickerView rowSizeForComponent:component].width, + [pickerView rowSizeForComponent:component].height, + } + }]; + } + + label.font = _font; + label.textColor = _color; + label.textAlignment = _textAlign; + label.text = [self pickerView:pickerView titleForRow:row forComponent:component]; + return label; } - (void)pickerView:(__unused UIPickerView *)pickerView @@ -82,7 +99,7 @@ numberOfRowsInComponent:(__unused NSInteger)component if (_onChange) { _onChange(@{ @"newIndex": @(row), - @"newValue": [self valueForRow:row] + @"newValue": RCTNullIfNil(_items[row][@"value"]), }); } } diff --git a/React/Views/RCTPickerManager.m b/React/Views/RCTPickerManager.m index 79d9c4758..759a05b6d 100644 --- a/React/Views/RCTPickerManager.m +++ b/React/Views/RCTPickerManager.m @@ -25,6 +25,24 @@ RCT_EXPORT_MODULE() RCT_EXPORT_VIEW_PROPERTY(items, NSDictionaryArray) RCT_EXPORT_VIEW_PROPERTY(selectedIndex, NSInteger) RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock) +RCT_EXPORT_VIEW_PROPERTY(color, UIColor) +RCT_EXPORT_VIEW_PROPERTY(textAlign, NSTextAlignment) +RCT_CUSTOM_VIEW_PROPERTY(fontSize, CGFloat, RCTPicker) +{ + view.font = [RCTConvert UIFont:view.font withSize:json ?: @(defaultView.font.pointSize)]; +} +RCT_CUSTOM_VIEW_PROPERTY(fontWeight, NSString, __unused RCTPicker) +{ + view.font = [RCTConvert UIFont:view.font withWeight:json]; // defaults to normal +} +RCT_CUSTOM_VIEW_PROPERTY(fontStyle, NSString, __unused RCTPicker) +{ + view.font = [RCTConvert UIFont:view.font withStyle:json]; // defaults to normal +} +RCT_CUSTOM_VIEW_PROPERTY(fontFamily, NSString, RCTPicker) +{ + view.font = [RCTConvert UIFont:view.font withFamily:json ?: defaultView.font.familyName]; +} - (NSDictionary *)constantsToExport {