Skip to content

Commit 3fdddb6

Browse files
authored
feat: numberOfLines prop on iOS (#291)
1 parent 9712d4d commit 3fdddb6

File tree

13 files changed

+114
-27
lines changed

13 files changed

+114
-27
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,12 +292,12 @@ Style to apply to each of the item labels.
292292

293293
### `numberOfLines`
294294

295-
On Android, used to truncate the text with an ellipsis after computing the text layout, including line wrapping,
295+
On Android & iOS, used to truncate the text with an ellipsis after computing the text layout, including line wrapping,
296296
such that the total number of lines does not exceed this number. Default is '1'
297297

298298
| Type | Required | Platform |
299299
| ------- | -------- | -------- |
300-
| number | No | Android |
300+
| number | No | Android, iOS |
301301

302302
### `onBlur`
303303

example/ios/PickerExample.xcodeproj/project.pbxproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,7 @@
561561
COPY_PHASE_STRIP = NO;
562562
ENABLE_STRICT_OBJC_MSGSEND = YES;
563563
ENABLE_TESTABILITY = YES;
564-
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
564+
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "arm64 ";
565565
GCC_C_LANGUAGE_STANDARD = gnu99;
566566
GCC_DYNAMIC_NO_PIC = NO;
567567
GCC_NO_COMMON_BLOCKS = YES;
@@ -626,7 +626,7 @@
626626
COPY_PHASE_STRIP = YES;
627627
ENABLE_NS_ASSERTIONS = NO;
628628
ENABLE_STRICT_OBJC_MSGSEND = YES;
629-
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
629+
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "arm64 ";
630630
GCC_C_LANGUAGE_STANDARD = gnu99;
631631
GCC_NO_COMMON_BLOCKS = YES;
632632
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;

example/ios/Podfile.lock

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ PODS:
316316
- React-cxxreact (= 0.64.2)
317317
- React-jsi (= 0.64.2)
318318
- React-perflogger (= 0.64.2)
319-
- RNCPicker (1.16.1):
319+
- RNCPicker (2.1.0):
320320
- React-Core
321321
- Yoga (1.14.0)
322322
- YogaKit (1.18.1):
@@ -454,7 +454,7 @@ SPEC CHECKSUMS:
454454
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
455455
DoubleConversion: cf9b38bf0b2d048436d9a82ad2abe1404f11e7de
456456
FBLazyVector: e686045572151edef46010a6f819ade377dfeb4b
457-
FBReactNativeSpec: 84eae1db5dca3a17141275c7d629d712c197ce2e
457+
FBReactNativeSpec: 09ba75493e18a703654c11b0a2f4c096fb1c13d5
458458
Flipper: d3da1aa199aad94455ae725e9f3aa43f3ec17021
459459
Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41
460460
Flipper-Folly: 755929a4f851b2fb2c347d533a23f191b008554c
@@ -488,7 +488,7 @@ SPEC CHECKSUMS:
488488
React-RCTVibration: 24600e3b1aaa77126989bc58b6747509a1ba14f3
489489
React-runtimeexecutor: a9904c6d0218fb9f8b19d6dd88607225927668f9
490490
ReactCommon: 149906e01aa51142707a10665185db879898e966
491-
RNCPicker: 61c7a0645b9e4ac67960aa7f22a3effcecfdfb8d
491+
RNCPicker: f7a40b21b915b7a187624d52f52b7bc2f73ea413
492492
Yoga: 575c581c63e0d35c9a83f4b46d01d63abc1100ac
493493
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
494494

ios/RNCPicker.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
#import <React/UIView+React.h>
1111

12+
#import "RNCPickerLabel.h"
13+
1214
@interface RNCPicker : UIPickerView
1315

1416
@property (nonatomic, copy) NSArray<NSDictionary *> *items;
@@ -18,6 +20,8 @@
1820
@property (nonatomic, strong) UIFont *font;
1921
@property (nonatomic, assign) NSTextAlignment textAlign;
2022

23+
@property (nonatomic, assign) NSInteger numberOfLines;
24+
2125
@property (nonatomic, copy) RCTBubblingEventBlock onChange;
2226

2327
@end

ios/RNCPicker.m

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ - (instancetype)initWithFrame:(CGRect)frame
2222
_font = [UIFont systemFontOfSize:21]; // TODO: selected title default should be 23.5
2323
_selectedIndex = NSNotFound;
2424
_textAlign = NSTextAlignmentCenter;
25+
_numberOfLines = 1;
2526
self.delegate = self;
2627
self.dataSource = self;
2728
[self selectRow:0 inComponent:0 animated:YES]; // Workaround for missing selection indicator lines (see https://stackoverflow.com/questions/39564660/uipickerview-selection-indicator-not-visible-in-ios10)
@@ -48,6 +49,20 @@ - (void)setSelectedIndex:(NSInteger)selectedIndex
4849
}
4950
}
5051

52+
- (void)setNumberOfLines:(NSInteger)numberOfLines
53+
{
54+
_numberOfLines = numberOfLines;
55+
[self reloadAllComponents];
56+
[self setNeedsLayout];
57+
}
58+
59+
- (void) setFont:(UIFont *)font
60+
{
61+
_font = font;
62+
[self reloadAllComponents];
63+
[self setNeedsLayout];
64+
}
65+
5166
#pragma mark - UIPickerViewDataSource protocol
5267

5368
- (NSInteger)numberOfComponentsInPickerView:(__unused UIPickerView *)pickerView
@@ -70,33 +85,44 @@ - (NSString *)pickerView:(__unused UIPickerView *)pickerView
7085
return [RCTConvert NSString:_items[row][@"label"]];
7186
}
7287

73-
- (CGFloat)pickerView:(__unused UIPickerView *)pickerView rowHeightForComponent:(NSInteger)__unused component {
74-
return _font.pointSize + 19;
88+
- (CGFloat)pickerView:(__unused UIPickerView *)pickerView rowHeightForComponent:(__unused NSInteger) component {
89+
return (_font.lineHeight) * _numberOfLines + 20;
7590
}
7691

7792
- (UIView *)pickerView:(UIPickerView *)pickerView
7893
viewForRow:(NSInteger)row
7994
forComponent:(NSInteger)component
80-
reusingView:(UILabel *)label
95+
reusingView:(UIView *)view
8196
{
82-
if (!label) {
83-
label = [[UILabel alloc] initWithFrame:(CGRect){
84-
CGPointZero,
85-
{
86-
[pickerView rowSizeForComponent:component].width,
87-
[pickerView rowSizeForComponent:component].height,
88-
}
89-
}];
97+
if (!view) {
98+
CGFloat rowHeight = [pickerView rowSizeForComponent:component].height;
99+
CGFloat rowWidth = [pickerView rowSizeForComponent:component].width;
100+
view = [[UIView alloc] initWithFrame:CGRectZero];
101+
RNCPickerLabel* label = [[RNCPickerLabel alloc] initWithFrame:(CGRect) {
102+
CGPointZero,
103+
{
104+
rowWidth,
105+
rowHeight,
106+
}
107+
}];
108+
[view insertSubview:label atIndex:0];
90109
}
91110

111+
RNCPickerLabel* label = view.subviews[0];
92112
label.font = _font;
93113

94114
label.textColor = [RCTConvert UIColor:_items[row][@"textColor"]] ?: _color;
95115

96116
label.textAlignment = _textAlign;
97117
label.text = [self pickerView:pickerView titleForRow:row forComponent:component];
98118
label.accessibilityIdentifier = _items[row][@"testID"];
99-
return label;
119+
120+
label.numberOfLines = _numberOfLines;
121+
122+
label.leftInset = 20.0;
123+
label.rightInset = 20.0;
124+
125+
return view;
100126
}
101127

102128
- (void)pickerView:(__unused UIPickerView *)pickerView

ios/RNCPicker.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
/* Begin PBXBuildFile section */
1010
65A65136236317E000467FDE /* RNCPickerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 65A65135236317E000467FDE /* RNCPickerManager.m */; };
11+
83CE2A3626889B7700470183 /* RNCPickerLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CE2A3526889B7700470183 /* RNCPickerLabel.m */; };
1112
B3E7B58A1CC2AC0600A0062D /* RNCPicker.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RNCPicker.m */; };
1213
/* End PBXBuildFile section */
1314

@@ -27,6 +28,8 @@
2728
134814201AA4EA6300B7C361 /* libRNCPicker.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNCPicker.a; sourceTree = BUILT_PRODUCTS_DIR; };
2829
65A65134236317E000467FDE /* RNCPickerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCPickerManager.h; sourceTree = "<group>"; };
2930
65A65135236317E000467FDE /* RNCPickerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNCPickerManager.m; sourceTree = "<group>"; };
31+
83CE2A3426889B7700470183 /* RNCPickerLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCPickerLabel.h; sourceTree = "<group>"; };
32+
83CE2A3526889B7700470183 /* RNCPickerLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNCPickerLabel.m; sourceTree = "<group>"; };
3033
B3E7B5881CC2AC0600A0062D /* RNCPicker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCPicker.h; sourceTree = "<group>"; };
3134
B3E7B5891CC2AC0600A0062D /* RNCPicker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNCPicker.m; sourceTree = "<group>"; };
3235
/* End PBXFileReference section */
@@ -53,6 +56,8 @@
5356
58B511D21A9E6C8500147676 = {
5457
isa = PBXGroup;
5558
children = (
59+
83CE2A3426889B7700470183 /* RNCPickerLabel.h */,
60+
83CE2A3526889B7700470183 /* RNCPickerLabel.m */,
5661
65A65134236317E000467FDE /* RNCPickerManager.h */,
5762
65A65135236317E000467FDE /* RNCPickerManager.m */,
5863
B3E7B5881CC2AC0600A0062D /* RNCPicker.h */,
@@ -118,6 +123,7 @@
118123
isa = PBXSourcesBuildPhase;
119124
buildActionMask = 2147483647;
120125
files = (
126+
83CE2A3626889B7700470183 /* RNCPickerLabel.m in Sources */,
121127
B3E7B58A1CC2AC0600A0062D /* RNCPicker.m in Sources */,
122128
65A65136236317E000467FDE /* RNCPickerManager.m in Sources */,
123129
);

ios/RNCPickerLabel.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#import <UIKit/UIKit.h>
2+
3+
@interface RNCPickerLabel : UILabel
4+
5+
@property (nonatomic, assign) CGFloat topInset;
6+
@property (nonatomic, assign) CGFloat bottomInset;
7+
@property (nonatomic, assign) CGFloat leftInset;
8+
@property (nonatomic, assign) CGFloat rightInset;
9+
10+
@end

ios/RNCPickerLabel.m

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#import "RNCPickerLabel.h"
2+
3+
@implementation RNCPickerLabel
4+
5+
- (instancetype)initWithFrame:(CGRect)frame
6+
{
7+
self = [super initWithFrame:frame];
8+
if (self) {
9+
self.topInset = 0.0;
10+
self.bottomInset = 0.0;
11+
self.leftInset = 0.0;
12+
self.rightInset = 0.0;
13+
}
14+
return self;
15+
}
16+
17+
- (void)drawTextInRect:(CGRect)rect
18+
{
19+
UIEdgeInsets insets = UIEdgeInsetsMake(self.topInset, self.leftInset, self.bottomInset, self.rightInset);
20+
[super drawTextInRect:UIEdgeInsetsInsetRect(rect, insets)];
21+
}
22+
23+
- (CGSize)intrinsicContentSize
24+
{
25+
CGSize intrinsicSuperViewContentSize = [super intrinsicContentSize];
26+
intrinsicSuperViewContentSize.height += self.topInset + self.bottomInset;
27+
intrinsicSuperViewContentSize.width += self.leftInset + self.rightInset;
28+
return intrinsicSuperViewContentSize;
29+
}
30+
31+
@end

ios/RNCPickerManager.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ - (UIView *)view
2525
RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock)
2626
RCT_EXPORT_VIEW_PROPERTY(color, UIColor)
2727
RCT_EXPORT_VIEW_PROPERTY(textAlign, NSTextAlignment)
28+
RCT_EXPORT_VIEW_PROPERTY(numberOfLines, NSInteger)
2829
RCT_CUSTOM_VIEW_PROPERTY(fontSize, NSNumber, RNCPicker)
2930
{
3031
view.font = [RCTFont updateFont:view.font withSize:json ?: @(defaultView.font.pointSize)];

js/PickerIOS.ios.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ type RNCPickerIOSType = HostComponent<
4545
selectedIndex: number,
4646
style?: ?TextStyleProp,
4747
testID?: ?string,
48+
numberOfLines?: ?number,
4849
|}>,
4950
>;
5051

@@ -57,6 +58,7 @@ type Props = $ReadOnly<{|
5758
onChange?: ?(event: PickerIOSChangeEvent) => mixed,
5859
onValueChange?: ?(itemValue: string | number, itemIndex: number) => mixed,
5960
selectedValue: ?(number | string),
61+
numberOfLines: ?number,
6062
|}>;
6163

6264
type State = {|
@@ -103,6 +105,10 @@ class PickerIOS extends React.Component<Props, State> {
103105
}
104106

105107
render(): React.Node {
108+
let numberOfLines = Math.round(this.props.numberOfLines ?? 1);
109+
if (numberOfLines < 1) {
110+
numberOfLines = 1;
111+
}
106112
return (
107113
<View style={this.props.style}>
108114
<RNCPickerNativeComponent
@@ -114,6 +120,7 @@ class PickerIOS extends React.Component<Props, State> {
114120
items={this.state.items}
115121
selectedIndex={this.state.selectedIndex}
116122
onChange={this._onChange}
123+
numberOfLines={numberOfLines}
117124
/>
118125
</View>
119126
);

0 commit comments

Comments
 (0)