Channels ▼
RSS

Mobile

Improving Data Entry with UI Pickers in iOS


Adding the Pickers

Next on the code renovations is modifying the TempTableController class. The basic plan is to comment out the textFieldShouldReturn method and add textFieldShouldBeginEditing, which implements the pickers. We'll also have to provide some support routines and display a "Done" button. Start with the TempTableController.h file, and change the class interface declaration to

@interface TempTableController : UITableViewController <UITextFieldDelegate, UIPickerViewDelegate, UIPickerViewDataSource>

This change specifies that the TempTableController class acts as the delegate for the UIPickerViewDataSource and UIPickerViewDelegate protocols. The class still remains the delegate for the UITextFieldDelegate protocol, since we use textFieldShouldBeginEditing to kick off the display of the picker.

Now add the following objects to the appropriate sections of the TempTableController.h file (Listing Three), while leaving the existing objects in place:

Listing Three

UIDatePicker *timeView;
UIPickerView *tempPickerView;
NSArray      *setPointPickerViewArray;
UIBarButtonItem *doneButton;		// This button appears only when the
									//   date picker is open
UIBarButtonItem *doneTempButton;	// This button appears only when
									//   the temperature picker is open

@property (nonatomic, retain) UIBarButtonItem *doneButton;
@property (nonatomic, retain) UIBarButtonItem *doneTempButton;

@property (nonatomic, retain) UIDatePicker *timeView;
@property (nonatomic, retain) UIPickerView *tempPickerView;
@property (nonatomic, retain) NSArray *setPointPickerViewArray;

These declarations define a UIPickerDate and UIPickerView, plus the array, setPointPickerViewArray, which stores the content for the picker component that displays the temperatures.

Proceed to the TempTableController.m file and add the support for these new objects with the Objective-C synthesize statements. Next, revise the viewDidLoad method by entering the following code at the end of the method (Listing Four):

Listing Four

self.title = [NSString stringWithFormat:@"Thermostat"];
	
// Set up for temp picker
// Choose temperature scale to display
setPointPickerViewArray = [[NSArray arrayWithObjects:
	@"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", nil] retain];

The code in Listing Four assigns a title to the view that displays the table. This title will appear in the navigation bar. The array, setPointPickerViewArray, is populated with temperature values. It provides the content for the temperature picker component. The picker will display these temperatures and allow the user to make a selection. Note that only valid temperatures make up the array, which eliminates the need to check the accuracy of the picker choice. For the time setting picker, we can pull the values straight from the table's data model as content for its components. Check the code in the setUpSchedule method for more information on the time array; there is no reason to modify it.

Next, we assign message actions to the "Done" buttons in the formerly empty viewWillAppear method (Listing Five):

Listing Five

- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];

    // Have done buttons for the Picker choice ready to go
    doneButton = [[UIBarButtonItem alloc]
		initWithBarButtonSystemItem:UIBarButtonSystemItemDone
		target:self
		action:@selector(doneAction:)];
	doneTempButton = [[UIBarButtonItem alloc]
		initWithBarButtonSystemItem:UIBarButtonSystemItemDone
		target:self
		action:@selector(doneTempAction:)];
}

The methods doneAction and doneTempAction remove the "Done" button and write the values chosen by the pickers into the table data arrays. If you build the app now, you'll get warning messages from XCode about the missing methods numberOfComponentsInPickerView and numberOfRowsInComponent. However, you can run the app and the title "Themostat" should appear in the navigation bar.

Now comment out the textFieldShouldReturn method, and start entering code for a textFieldShouldBeginEditing method (Listing Six).

Listing Six

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
		
    // Derive indexpath from info in tag so that the row can
    // be repositioned so that the picker does not hide it
    NSUInteger tag = textField.tag;
    NSUInteger row = (tag % 100) / 10;
    NSUInteger section = (tag / 100);
	
    NSUInteger moveRowIndex[] = {section, row};
    NSIndexPath *moveIndexPath = [[NSIndexPath alloc]
		initWithIndexes:moveRowIndex length:2];
	
    // If true, user clicked on temp field. Bring up temperature picker.
    if ((textField.tag & 1) == 0) {
        	// First, verify that user isn't try to change to a date
			// picker on us while temp picker is on-screen. Bail if so.
        if (self.timeView.superview != nil) {
		    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil
			 message:@"Tap Done to dismiss the time picker first."
			 delegate:self
			 cancelButtonTitle:@"Cancel"
			 otherButtonTitles:nil];
		    [alert show];
		    [alert release];
		    return NO;
	  }

This section of code extracts the table section, row, and the field that the user tapped on. It does this by filtering values from the tag property of the textField view. For more information on how this information was encoded into the tag values, consult the DDJ article, Making Two-Way Tables in iOS. The next step is to determine which textField was tapped on, as we need to know whether to call up a UIPickerView or a UIDatePicker. Fortunately, the field values were carefully chosen so that the temperature fields have even values. By applying a logical AND to the tag value and testing the least significant bit (a check for an odd value), we can quickly determine the picker required.

Assuming that it's a temperature field, the next step is to check if the instance of UIDatePicker, timeView, is on screen already. This occurs when the user, having changed a time setting, now taps on a temperature field instead of dismissing the time picker with the "Done" button. The test for an active UIDatePicker is by using superview to sniff out the presence of its view. If this is the case, the code presents a warning dialog with an explanation as to what to do and exits. The code performs a similar check when, after changing a temperature setting, the user taps on a time field.

Now we add code to see if the temperature picker, tempPickerView, is already on-screen. If not, then we allocate the picker (Listing Seven):

Listing Seven

	if (self.tempPickerView.superview == nil) {
			
	    // Make temperature picker
	    tempPickerView = [UIPickerView alloc] initWithFrame:CGRectZero];
	    CGSize pickerSize = [tempPickerView sizeThatFits:CGSizeZero];
	    tempPickerView.frame = [self pickerFrameWithSize:pickerSize];
			
	    tempPickerView.autoresizingMask =
			 UIViewAutoresizingFlexibleWidth;
		tempPickerView.showsSelectionIndicator = YES;
			
		// this view controller is the data source and delegate
		tempPickerView.delegate = self;
		tempPickerView.dataSource = self;
    }
			 // Add Done button to handle when choice is done
	self.navigationItem.rightBarButtonItem = doneTempButton;

    // Ensure that the tag value gets forwarded to the action handler
	self.tempPickerView.tag = textField.tag;

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video