-
-
Notifications
You must be signed in to change notification settings - Fork 412
Expand file tree
/
Copy pathRNDatePickerManager.mm
More file actions
241 lines (189 loc) · 9.25 KB
/
RNDatePickerManager.mm
File metadata and controls
241 lines (189 loc) · 9.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
#include <string>
#import <React/RCTLog.h>
#import <React/RCTUIManager.h>
#import <React/RCTViewManager.h>
#import "RNDatePickerManager.h"
#import "RCTConvert.h"
#import "DatePicker.h"
#import "RNDatePicker.h"
@implementation RCTConvert(UIDatePicker)
RCT_ENUM_CONVERTER(UIDatePickerMode, (@{
@"time": @(UIDatePickerModeTime),
@"date": @(UIDatePickerModeDate),
@"datetime": @(UIDatePickerModeDateAndTime),
@"countdown": @(UIDatePickerModeCountDownTimer), // not supported yet
}), UIDatePickerModeTime, integerValue)
@end
@implementation RNDatePickerManager
RCT_EXPORT_MODULE(RNDatePicker)
RCT_EXPORT_METHOD(addListener : (NSString *)eventName) {
// Keep: Required for RN built in Event Emitter Calls.
}
RCT_EXPORT_METHOD(removeListeners : (NSInteger)count) {
// Keep: Required for RN built in Event Emitter Calls.
}
- (NSDate *)convertToNSDate:(id)input {
if (!input) return nil;
std::string isoString = [input UTF8String];
NSString *nsString = [NSString stringWithUTF8String:isoString.c_str()];
NSISO8601DateFormatter *isoFormatter = [[NSISO8601DateFormatter alloc] init];
isoFormatter.formatOptions = NSISO8601DateFormatWithInternetDateTime | NSISO8601DateFormatWithFractionalSeconds;
NSDate *date = [isoFormatter dateFromString:nsString];
return date;
}
- (UIView *)view
{
return [RNDatePicker new];
}
RCT_EXPORT_VIEW_PROPERTY(text, NSString)
RCT_CUSTOM_VIEW_PROPERTY(date, id, DatePicker)
{
NSDate *date = [self convertToNSDate:json];
if (date) {
[view setDate:date];
}
}
RCT_EXPORT_VIEW_PROPERTY(locale, NSLocale)
RCT_CUSTOM_VIEW_PROPERTY(minimumDate, id, DatePicker)
{
NSDate *date = [self convertToNSDate:json];
if (date) {
[view setMinimumDate:date];
}
}
RCT_CUSTOM_VIEW_PROPERTY(maximumDate, id, DatePicker)
{
NSDate *date = [self convertToNSDate:json];
if (date) {
[view setMaximumDate:date];
}
}
RCT_EXPORT_VIEW_PROPERTY(minuteInterval, NSInteger)
RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock)
RCT_REMAP_VIEW_PROPERTY(mode, datePickerMode, UIDatePickerMode)
RCT_CUSTOM_VIEW_PROPERTY(timeZoneOffsetInMinutes, NSString, DatePicker)
{
[view setTimeZoneOffsetInMinutes:[RCTConvert NSString:json]];
}
RCT_CUSTOM_VIEW_PROPERTY(textColor, NSString, DatePicker)
{
[view setTextColorProp:[RCTConvert NSString:json]];
}
RCT_EXPORT_METHOD(openPicker:(NSDictionary *) props
onConfirm:(RCTResponseSenderBlock) onConfirm
onCancel:(RCTResponseSenderBlock) onCancel)
{
dispatch_async(dispatch_get_main_queue(), ^{
bool iPad = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad;
UIViewController *rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController;
CGRect rootBounds = rootViewController.view.bounds;
NSString * title = [RCTConvert NSString:[props objectForKey:@"title"]];
title = [title isEqualToString:@""] ? nil : title;
NSString * confirmText = [RCTConvert NSString:[props objectForKey:@"confirmText"]];
NSString * cancelText = [RCTConvert NSString:[props objectForKey:@"cancelText"]];
DatePicker* picker = [[DatePicker alloc] init];
[picker setup];
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleActionSheet];
UIView * alertView = alertController.view;
CGRect pickerBounds = picker.bounds;
// Detect the content size category
UIContentSizeCategory contentSize = UIApplication.sharedApplication.preferredContentSizeCategory;
int heightIncrement = 0;
// Adjust height based on content size category
if ([contentSize isEqualToString:UIContentSizeCategoryAccessibilityLarge]) {
heightIncrement = 15;
} else if ([contentSize isEqualToString:UIContentSizeCategoryAccessibilityExtraLarge]) {
heightIncrement = 40;
} else if ([contentSize isEqualToString:UIContentSizeCategoryAccessibilityExtraExtraLarge]) {
heightIncrement = 70;
} else if ([contentSize isEqualToString:UIContentSizeCategoryAccessibilityExtraExtraExtraLarge]) {
heightIncrement = 90;
}
// height
double pickerHeight = [self getPickerHeight:alertView];
int alertHeightPx = iPad ? (title ? 300 : 260) : (title ? 370 + heightIncrement : 340 + heightIncrement);
NSLayoutConstraint *height = [NSLayoutConstraint constraintWithItem:alertView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:alertHeightPx];
[alertView addConstraint:height];
pickerBounds.size.height = pickerHeight;
// width
double pickerWidth = [self getPickerWidth:alertView];
int alertWidthPx = pickerWidth;
NSLayoutConstraint *width = [NSLayoutConstraint constraintWithItem:alertView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:alertWidthPx];
[alertView addConstraint:width];
pickerBounds.size.width = pickerWidth;
// top padding
pickerBounds.origin.y += iPad ? (title ? 20: 5) : (title ? 30 : 10);
[picker setFrame: pickerBounds];
NSDate * _Nonnull date = [RCTConvert NSDate:[props objectForKey:@"date"]];
[picker setDate:date];
NSDate * minimumDate = [RCTConvert NSDate:[props objectForKey:@"minimumDate"]];
if(minimumDate) [picker setMinimumDate:minimumDate];
NSDate * maximumDate = [RCTConvert NSDate:[props objectForKey:@"maximumDate"]];
if(maximumDate) [picker setMaximumDate:maximumDate];
NSString * textColor = [RCTConvert NSString:[props objectForKey:@"textColor"]];
if(textColor) [picker setTextColorProp:textColor];
UIDatePickerMode mode = [RCTConvert UIDatePickerMode:[props objectForKey:@"mode"]];
[picker setDatePickerMode:mode];
NSLocale * locale = [RCTConvert NSLocale:[props objectForKey:@"locale"]];
if(locale) [picker setLocale:locale];
int minuteInterval = [RCTConvert int:[props objectForKey:@"minuteInterval"]];
[picker setMinuteInterval:minuteInterval];
NSString * timeZoneProp = [props valueForKey:@"timeZoneOffsetInMinutes"];
if(timeZoneProp) [picker setTimeZoneOffsetInMinutes:timeZoneProp];
if(@available(iOS 13, *)) {
NSString * _Nonnull theme = [RCTConvert NSString:[props objectForKey:@"theme"]];
if ([theme isEqualToString:@"light"]) {
alertController.overrideUserInterfaceStyle = UIUserInterfaceStyleLight;
} else if ([theme isEqualToString:@"dark"]) {
alertController.overrideUserInterfaceStyle = UIUserInterfaceStyleDark;
} else {
alertController.overrideUserInterfaceStyle = UIUserInterfaceStyleUnspecified;
}
}
[alertView addSubview:picker];
UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:confirmText style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
onConfirm(@[@{ @"timestamp": @(picker.date.timeIntervalSince1970 * 1000.0) }]);
}];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:cancelText style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
onCancel(@[]);
}];
[alertController addAction:cancelAction];
[alertController addAction:confirmAction];
if (@available(iOS 9.0, *)) {
alertController.preferredAction = confirmAction;
}
// ipad needs to display the picker in a popover
if (iPad) {
UIPopoverPresentationController *popPresenter = [alertController popoverPresentationController];
popPresenter.sourceRect = CGRectMake(CGRectGetMidX(rootBounds), CGRectGetMidY(rootBounds),0,0);
popPresenter.sourceView = rootViewController.view;
popPresenter.presentingViewController.preferredContentSize = CGSizeMake(pickerWidth, alertHeightPx);
[popPresenter setPermittedArrowDirections: (UIPopoverArrowDirection) 0];
}
// Finding the top view controller which is neccessary to be able to show the picker from within modal
self->_topViewController = rootViewController;
while (self->_topViewController.presentedViewController){
self->_topViewController = self->_topViewController.presentedViewController;
}
[self->_topViewController presentViewController:alertController animated:YES completion:nil];
});
}
RCT_EXPORT_METHOD(closePicker)
{
dispatch_async(dispatch_get_main_queue(), ^{
[self->_topViewController dismissViewControllerAnimated:YES completion:nil];
});
}
- (double) getPickerWidth :(UIView *) alertView
{
bool iPad = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad;
bool isLandscape = UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation);
if(iPad) return 320;
if (isLandscape) return 320;
return alertView.bounds.size.width - 15;
}
- (double) getPickerHeight :(UIView *) alertView
{
return 216;
}
@end