Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Refactored iOS DatePicker plugin #363

Merged
merged 5 commits into from

2 participants

@samsoir

This is a major refactor of the iOS DatePicker plugin to make it play nice with iOS. The previous version was not implementing the standard Objective-C patterns for memory management or delegation. This refactor provides the following;

  • Better memory management of all native iOS components (not using ARC)
  • Uses the UIActionSheetDelegate protocol to send selected date back to PhoneGap
  • Unloads the logic from the Back button segmented control
  • Uses ISO standard dates for communication between Objective C and JS to ensure no ambiguity
  • If no mode is sent to the control, UIDatePickerModeDateAndTime is used
samsoir added some commits
@samsoir samsoir Updated the DatePicker class to use standard iOS conventions for dele…
…gation and memory management
b86ff81
@samsoir samsoir Fixed a few minor errors with releasing to early. Also ensure the JS …
…returns the date to the PG JS client
009971f
@samsoir samsoir Using ISO standard date formatting within the iOS DatePicker.m class 396e3f4
@samsoir samsoir Created a new datetime mode to allow date and time to be sent 3d12767
@samsoir samsoir More refactoring of the DatePicker implmentation. Created one init me…
…thod for the ActionSheet that accepts its dependencies via DI. All date handling is now internationally recognised ISO. DatePicker will default to UIDatePickerModeDateAndTime unless specified otherwise. All memory is managed properly, making DatePicker plugin play nice in iOS (without ARC)
76a724e
@shazron shazron merged commit d07bc00 into phonegap:master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 22, 2012
  1. @samsoir

    Updated the DatePicker class to use standard iOS conventions for dele…

    samsoir authored
    …gation and memory management
  2. @samsoir

    Fixed a few minor errors with releasing to early. Also ensure the JS …

    samsoir authored
    …returns the date to the PG JS client
  3. @samsoir
  4. @samsoir
  5. @samsoir

    More refactoring of the DatePicker implmentation. Created one init me…

    samsoir authored
    …thod for the ActionSheet that accepts its dependencies via DI. All date handling is now internationally recognised ISO. DatePicker will default to UIDatePickerModeDateAndTime unless specified otherwise. All memory is managed properly, making DatePicker plugin play nice in iOS (without ARC)
This page is out of date. Refresh to see the latest.
View
11 iPhone/DatePicker/DatePicker.h
@@ -9,16 +9,21 @@
#import "PGPlugin.h"
#endif
+#ifndef k_DATEPICKER_DATETIME_FORMAT
+#define k_DATEPICKER_DATETIME_FORMAT @"yyyy-MM-dd'T'HH:mm:ss'Z'"
+#endif
-@interface DatePicker : PGPlugin {
- UIActionSheet *datePickerSheet;
- UIDatePicker *datePicker;
+@interface DatePicker : PGPlugin <UIActionSheetDelegate> {
+ UIActionSheet *_datePickerSheet;
+ UIDatePicker *_datePicker;
+ NSDateFormatter *_isoDateFormatter;
BOOL isVisible;
}
@property (nonatomic, retain) UIActionSheet* datePickerSheet;
@property (nonatomic, retain) UIDatePicker* datePicker;
+@property (nonatomic, retain) NSDateFormatter* isoDateFormatter;
//- (void) prepare:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
View
15 iPhone/DatePicker/DatePicker.js
@@ -15,11 +15,22 @@ if (typeof PhoneGap !== "undefined") {
* show - true to show the ad, false to hide the ad
*/
DatePicker.prototype.show = function(options, cb) {
+ var padDate = function(date) {
+ if (date.length == 1) {
+ return ("0" + date);
+ }
+ return date;
+ };
+
if (options.date) {
- options.date = (options.date.getMonth()+1)+"/"+(options.date.getDate())+"/"+(options.date.getFullYear());
+ options.date = options.date.getFullYear() + "-" +
+ padDate(options.date.getMonth()+1) + "-" +
+ padDate(options.date.getDate()) +
+ "T" + padDate(options.date.getHours()) + ":" +
+ padDate(options.date.getMinutes()) + ":00Z";
}
var defaults = {
- mode: '',
+ mode: 'datetime',
date: '',
allowOldDates: true
}
View
208 iPhone/DatePicker/DatePicker.m
@@ -1,79 +1,185 @@
// Phonegap DatePicker Plugin
// Copyright (c) Greg Allen 2011
// MIT Licensed
+//
+// Additional refactoring by Sam de Freyssinet
#import "DatePicker.h"
+@interface DatePicker (Private)
+// Initialize the UIActionSheet with ID <UIActionSheetDelegate> delegate UIDatePicker datePicker (UISegmentedControl)closeButton
+- (void)initActionSheet:(id <UIActionSheetDelegate>)delegateOrNil datePicker:(UIDatePicker *)datePicker closeButton:(UISegmentedControl *)closeButton;
+
+// Creates the NSDateFormatter with NSString format and NSTimeZone timezone
+- (NSDateFormatter *)createISODateFormatter:(NSString *)format timezone:(NSTimeZone *)timezone;
+
+// Creates the UIDatePicker with NSMutableDictionary options
+- (UIDatePicker *)createDatePicker:(CGRect)pickerFrame;
+
+// Creates the UISegmentedControl with UIView parentView, NSString title, ID target and SEL action
+- (UISegmentedControl *)createActionSheetCloseButton:(NSString *)title target:(id)target action:(SEL)action;
+
+// Configures the UIDatePicker with the NSMutableDictionary options
+- (void)configureDatePicker:(NSMutableDictionary *)optionsOrNil;
+
+@end
@implementation DatePicker
-@synthesize datePickerSheet;
-@synthesize datePicker;
+@synthesize datePickerSheet = _datePickerSheet;
+@synthesize datePicker = _datePicker;
+@synthesize isoDateFormatter = _isoDateFormatter;
+
+#pragma mark - Public Methods
+
+- (PGPlugin *)initWithWebView:(UIWebView *)theWebView
+{
+ self = (DatePicker *)[super initWithWebView:theWebView];
+
+ if (self)
+ {
+ UIDatePicker *userDatePicker = [self createDatePicker:CGRectMake(0, 40, 0, 0)];
+ UISegmentedControl *datePickerCloseButton = [self createActionSheetCloseButton:@"Close" target:self action:@selector(dismissActionSheet:)];
+ NSDateFormatter *isoTimeFormatter = [self createISODateFormatter:k_DATEPICKER_DATETIME_FORMAT timezone:[NSTimeZone defaultTimeZone]];
+ self.datePicker = userDatePicker;
+ self.isoDateFormatter = isoTimeFormatter;
-#pragma mark -
-#pragma mark Public Methods
+ [self initActionSheet:self datePicker:userDatePicker closeButton:datePickerCloseButton];
+ }
+ return self;
+}
-- (void) show:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
+- (void)show:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
{
- if (isVisible)
- return;
- NSString *mode = [options objectForKey:@"mode"];
- NSString *dateString = [options objectForKey:@"date"];
-
- NSLog(@"Show Datepicker");
-
- datePickerSheet = [[UIActionSheet alloc] initWithTitle:nil delegate:nil cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:nil];
-
- [datePickerSheet setActionSheetStyle:UIActionSheetStyleBlackTranslucent];
-
- CGRect pickerFrame = CGRectMake(0, 40, 0, 0);
-
- self.datePicker = [[UIDatePicker alloc] initWithFrame:pickerFrame];
- bool allowOldDates = ([[options objectForKey:@"allowOldDates"] intValue] == 1)?YES:NO;
- if (!allowOldDates) {
- self.datePicker.minimumDate = [NSDate date];
+ if (isVisible) {
+ return;
}
- if ([mode isEqualToString:@"date"]) {
- self.datePicker.datePickerMode = UIDatePickerModeDate;
- NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
- [dateFormatter setTimeZone:[NSTimeZone defaultTimeZone]];
- [dateFormatter setDateFormat:@"MM/dd/yyyy"];
- NSDate *date = [dateFormatter dateFromString:dateString];
- [dateFormatter release];
- self.datePicker.date = date;
+ [self configureDatePicker:options];
+ [self.datePickerSheet showInView:[[super webView] superview]];
+ [self.datePickerSheet setBounds:CGRectMake(0, 0, 320, 485)];
+
+ isVisible = YES;
+}
+
+- (void)dismissActionSheet:(id)sender {
+ [self.datePickerSheet dismissWithClickedButtonIndex:0 animated:YES];
+}
+
+- (void)onMemoryWarning
+{
+ // It could be better to close the datepicker before the system
+ // clears memory. But in reality, other non-visible plugins should
+ // be tidying themselves at this point. This could cause a fatal
+ // at runtime.
+ if (isVisible) {
+ return;
}
- else if ([mode isEqualToString:@"time"])
- self.datePicker.datePickerMode = UIDatePickerModeTime;
-
- [datePickerSheet addSubview:self.datePicker];
-
- UISegmentedControl *closeButton = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObject:@"Close"]];
+
+ [self release];
+}
+
+- (void)dealloc
+{
+ [_datePicker release];
+ [_datePickerSheet release];
+ [_isoDateFormatter release];
+
+ [super dealloc];
+}
+
+#pragma mark - UIActionSheetDelegate methods
+
+- (void)actionSheet:(UIActionSheet *)actionSheet willDismissWithButtonIndex:(NSInteger)buttonIndex
+{
+ NSString* jsCallback = [NSString stringWithFormat:@"window.plugins.datePicker._dateSelected(\"%i\");", (int)[self.datePicker.date timeIntervalSince1970]];
+ [super writeJavascript:jsCallback];
+}
+
+- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
+{
+ isVisible = NO;
+}
+
+#pragma mark - Private Methods
+
+- (void)initActionSheet:(id <UIActionSheetDelegate>)delegateOrNil datePicker:(UIDatePicker *)datePicker closeButton:(UISegmentedControl *)closeButton
+{
+ UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:nil
+ delegate:delegateOrNil
+ cancelButtonTitle:nil
+ destructiveButtonTitle:nil
+ otherButtonTitles:nil];
+
+ [actionSheet setActionSheetStyle:UIActionSheetStyleBlackTranslucent];
+
+ [actionSheet addSubview:datePicker];
+ [actionSheet addSubview:closeButton];
+
+ self.datePickerSheet = actionSheet;
+
+ [actionSheet release];
+}
+
+- (UIDatePicker *)createDatePicker:(CGRect)pickerFrame
+{
+ UIDatePicker *datePickerControl = [[UIDatePicker alloc] initWithFrame:pickerFrame];
+ return [datePickerControl autorelease];
+}
+
+- (NSDateFormatter *)createISODateFormatter:(NSString *)format timezone:(NSTimeZone *)timezone;
+{
+ NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
+ [dateFormatter setTimeZone:timezone];
+ [dateFormatter setDateFormat:format];
+
+ return [dateFormatter autorelease];
+}
+
+- (UISegmentedControl *)createActionSheetCloseButton:(NSString *)title target:(id)target action:(SEL)action
+{
+ UISegmentedControl *closeButton = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObject:title]];
+
closeButton.momentary = YES;
closeButton.frame = CGRectMake(260, 7.0f, 50.0f, 30.0f);
closeButton.segmentedControlStyle = UISegmentedControlStyleBar;
closeButton.tintColor = [UIColor blackColor];
- [closeButton addTarget:self action:@selector(dismissActionSheet:) forControlEvents:UIControlEventValueChanged];
- [datePickerSheet addSubview:closeButton];
- [closeButton release];
-
- [datePickerSheet showInView:[[super webView] superview]];
-
- [datePickerSheet setBounds:CGRectMake(0, 0, 320, 485)];
- isVisible = YES;
+ [closeButton addTarget:target action:action forControlEvents:UIControlEventValueChanged];
+
+ return [closeButton autorelease];
}
-- (void) dismissActionSheet:(id)sender {
- [datePickerSheet dismissWithClickedButtonIndex:0 animated:YES];
- [datePickerSheet release];
- [datePicker release];
- NSString* jsCallback = [NSString stringWithFormat:@"window.plugins.datePicker._dateSelected(\"%i\");", (int)[self.datePicker.date timeIntervalSince1970]];
- [super writeJavascript:jsCallback];
- isVisible = NO;
+- (void)configureDatePicker:(NSMutableDictionary *)optionsOrNil;
+{
+ NSString *mode = [optionsOrNil objectForKey:@"mode"];
+ NSString *dateString = [optionsOrNil objectForKey:@"date"];
+ BOOL allowOldDates = NO;
+
+ if ([[optionsOrNil objectForKey:@"allowOldDates"] intValue] == 1) {
+ allowOldDates = YES;
+ }
+
+ if ( ! allowOldDates) {
+ self.datePicker.minimumDate = [NSDate date];
+ }
+
+ self.datePicker.date = [self.isoDateFormatter dateFromString:dateString];
+
+ if ([mode isEqualToString:@"date"]) {
+ self.datePicker.datePickerMode = UIDatePickerModeDate;
+ }
+ else if ([mode isEqualToString:@"time"])
+ {
+ self.datePicker.datePickerMode = UIDatePickerModeTime;
+ }
+ else
+ {
+ self.datePicker.datePickerMode = UIDatePickerModeDateAndTime;
+ }
}
@end
Something went wrong with that request. Please try again.