10.8. Date/Time Pickers

The UIDatePicker class is a control that encapsulates and customizes a UIPickerView class specifically to accept date, time, and duration input. The date picker automatically configures its columns to conform to the specified style, so there's no low-level work involved in configuring dials. You can also customize it for any range of dates.

The UIDatePicker relies heavily on the NSDate class, which is part of the foundation class set used in Cocoa on the desktop. More information about this class can be found in online Cocoa references from the Apple Developer Connection website. For the purpose of the examples used here, we'll create an NSDate using its simplest method, initWithString:

NSDate *myDate = [ [ NSDate alloc ]
    initWithString: @"1963-11-22 12:30:00 -0500" ];

10.8.1. Creating the Date/Time Picker

The UIDatePicker is much more straightforward than the standard UIPickerView. It builds its own data source based on the date ranges you specify. To use it, just create the object:

UIDatePicker *datePicker = [ [ UIDatePicker alloc ]
    initWithFrame: CGRectMake(0.0, 0.0, 0.0, 0.0)];

By default, the picker presents the current date and time, and presents dials for day of week with month and day, hour, minute, and AM/PM. The user can select any date or time combination by default. The following subsections explain further customizations to the picker's operation.

10.8.1.1. Date picker mode

The date/time picker supports four different selection modes. Define this mode by setting the datePickerMode property as follows:

datePicker datePickerMode = UIDatePickerModeTime;

The following modes are supported.

Mode Description
UIDatePickerModeTime Hour, minute, and AM/PM selection
UIDatePickerModeDate Month, day, and year
UIDatePickerModeDateAndTime Default; day of week + month + day, hour, minute, and AM/PM selection
UIDatePickerModeCountDownTimer Hour and minute display for timers

10.8.1.2. Time intervals

You can set the minute dial to display minutes in various intervals, as long as the interval divides evenly into 60. The default is to display the minute dial with one-minute intervals. To use a different interval, set the minuteInterval property to the desired interval:

datePicker.minuteInterval = 10;

10.8.1.3. Date ranges

You can specify a range of allowable dates by setting the minimumDate and maximumDate properties. If the user attempts to scroll to a date beyond this range, the dial will scroll back to the closest valid date. Both methods expect an NSDate object:

NSDate *minDate = [ [ NSDate alloc ]
    initWithString: @"1773-12-16 12:00:00 -0500" ];
NSDate *maxDate = [ [ NSDate alloc ]
    initWithString: @"1776-07-04 12:00:00 -0500" ];

datePicker.minimumDate = minDate;
datePicker.maximumDate = maxDate;

If one or both of these date range properties isn't set, the default behavior will allow the user to select any past or future date. This can be useful, for example, when selecting a birthday, which could be any date in the past, but capped at the current day.

To set the date you would like to be displayed by default, set the date property:

datePicker.date = minDate;

Alternatively, you may use the setDate method. If you choose to animate, the dials will scroll to the date you specify:

[ datePicker setDate: maxDate animated: YES ];

10.8.2. Displaying the Date Picker

Once you have created the date picker, attach it to a view object using the same method as that of the UIPickerView:

[ self addSubview: datePicker ];

The picker is always 216 pixels high, regardless of the frame size passed to it. You'll need to make sure you've allocated enough screen space to host it.

10.8.3. Reading the Date

The date is generally read from the date picker when the user transitions to a different view, such as leaving a preferences table. The date property provides an NSDate object when read:

NSDate *selectedDate = datePicker.date;

Because the date picker is a subclass of the UIControl class (unlike UIPickerView), you can also hook a delegate into the UIControl class's notification structure:

[ datePicker addTarget: self action:
    @selector(dateChanged:)
    forControlEvents:UIControlEventValueChanged
];

Your action class will be called whenever the user selects a new date:

-(void) dateChanged: (id)sender
{
    UIDatePicker *control = (UIDatePicker *) sender;
    NSDate *selectedDate = control.date;

  /* Additional code to respond to date change */
}

10.8.4. DatePicker: Independence Day Picker

This simple example illustrates the use of a basic date picker object to select a date between the Boston Tea Party (December 16, 1773) and American Independence Day (July 4, 1776). The example simply creates a UIDatePicker object and displays it to the user (Figure 10-7). When the user selects a new date, this information is updated in the text view above the picker.

Figure 10-7. DatePicker example


You can compile this application, shown in Examples Example 10-19 through Example 10-23, with the SDK by creating a view-based application project named DatePicker. Be sure to pull out the Interface Builder code if you'd like to see how all objects are created from scratch.

Example 10-19. DatePicker application delegate prototypes (DatePickerAppDelegate.h)
#import <UIKit/UIKit.h>

@class DatePickerViewController;

@interface DatePickerAppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow *window;
    DatePickerViewController *viewController;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet DatePickerViewController *viewController;

@end

                                          

Example 10-20. DatePicker application delegate (DatePickerAppDelegate.m)
#import "DatePickerAppDelegate.h"
#import "DatePickerViewController.h"

@implementation DatePickerAppDelegate

@synthesize window;
@synthesize viewController;


- (void)applicationDidFinishLaunching:(UIApplication *)application {
    CGRect screenBounds = [ [ UIScreen mainScreen ] bounds ];

    self.window = [ [ [ UIWindow alloc ] initWithFrame: screenBounds ] autorelease ];
    viewController = [ [ DatePickerViewController alloc ] init ];

    [ window addSubview: viewController.view ];
    [ window makeKeyAndVisible ];
}


- (void)dealloc {
    [ viewController release ];
    [ window release ];
    [ super dealloc ];
}


@end

                                          

Example 10-21. DatePicker view controller prototypes (DatePickerViewController.h)
#import <UIKit/UIKit.h>

@protocol UIPickerViewDataSource, UIPickerViewDelegate;

@interface DatePickerViewController : UIViewController {
    UIDatePicker *pickerView;
    UITextView *textView;
    NSDate *minDate, *maxDate;
}
-(void) dateChanged: (id)sender;

@end

Example 10-22. DatePicker view controller (DatePickerViewController.m)
#import "DatePickerViewController.h"

@implementation DatePickerViewController

- (id)init {
    self = [ super init ];
    if (self != nil) {
        minDate = [ [ NSDate alloc ]
            initWithString: @"1773-12-16 12:00:00 -0500" ];
        maxDate = [ [ NSDate alloc ]
            initWithString: @"1776-07-04 12:00:00 -0500" ];
    }
    return self;
}

- (void)loadView {
    CGRect bounds = [ [ UIScreen mainScreen ] applicationFrame ];

    [ super loadView ];

    pickerView = [ [ UIDatePicker alloc ] initWithFrame:
        CGRectMake(0.0, bounds.size.height - 216.0, 0.0, 0.0) ];

    pickerView.minimumDate = minDate;
    pickerView.maximumDate = maxDate;
    pickerView.datePickerMode = UIDatePickerModeDate;
    pickerView.date = minDate;

    [ pickerView addTarget: self action:
     @selector(dateChanged:)
          forControlEvents:UIControlEventValueChanged
    ];

    [ self.view addSubview: pickerView ];

    textView = [ [ UITextView alloc ] initWithFrame:
        CGRectMake(0.0, 0.0, bounds.size.width, bounds.size.height - 216.0) ];
    textView.font = [ UIFont fontWithName: @"Arial" size: 22.0 ];
    textView.textColor = [ UIColor redColor ];
    textView.textAlignment = UITextAlignmentCenter;
    textView.text = [ pickerView.date description ];
    textView.editable = NO;

    [ self.view addSubview: textView ];
}

-(void) dateChanged: (id)sender
{
    UIDatePicker *control = (UIDatePicker *) sender;
    NSDate *selectedDate = control.date;
    textView.text = [ selectedDate description ];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:
(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}


- (void)didReceiveMemoryWarning {
    [ super didReceiveMemoryWarning ];
}

- (void)dealloc {
    [ pickerView release ];
    [ super dealloc ];
}

@end

                                          

Example 10-23. DatePicker main (main.m)
#import <UIKit/UIKit.h>

int main(int argc, char *argv[]) {

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int retVal = UIApplicationMain(argc, argv, nil, @"DatePickerAppDelegate");
    [pool release];
    return retVal;
}

                                          

10.8.5. What's Going On

  1. When the application is run, its application delegate creates a new view controller, which, in turn, initializes a set of NSDate objects containing the minimum and maximum dates.

  2. When the view controller's loadView method is invoked, UIDatePicker and UITextView objects are created and added as subviews to the view controller. The date picker is initialized with minimum and maximum dates, and the current value is set to the minimum date. An observer is added to invoke the dateChanged method whenever the user selects a new date.

  3. The UIDatePicker class's internal plumbing builds the picker based on the dates and style specified.

  4. When the user selects a new item from any dial on the picker, the UIControl notification framework invokes the target's dateChanged method. This reads the new date and displays it in the text view above the date picker.

10.8.6. Further Study

When you've finished spinning dials, consider some additional reading:

  • Check out the UIDatePicker.h prototypes. You'll find these deep within /Developer/Platforms/iPhoneOS.platform, inside the UI Kit framework's Headers directory.