Because we’re dedicating an
entire hour to pickers (UIPickerView),
you can probably surmise that they’re not quite the same as the other UI
objects that we’ve been using. Pickers are a unique feature of the
iPhone. They present a series of multivalue options in a clever spinning
interface—frequently compared to a slot machine. Rather than fruit or
numbers, the segments, known as components, display
rows of values that the user can choose from. The closest desktop
equivalent is a set of pop-up menus. Figure 1 displays the standard date picker (UIDatePicker).
Pickers should be used when a
user needs to make a selection between multiple (usually related)
values. They are frequently used for setting dates and times but can be
customized to handle just about any selection option that you can come
up with.
Apple recognized that pickers are
a great option for choosing dates and times, so they’ve made them
available in two different forms: date pickers, which are easy to
implement and dedicated to handling dates and times; and custom picker
views that can be configured to display as many components as rows as
you’d like.
Date Pickers
The date picker (UIDatePicker), shown in Figure 1, is very similar to the other objects that we’ve
been using over the past few hours. To use it, we’ll add it to a view,
wait for the user to interact with it, and then read its value. Instead
of returning a string or integer, however, the date picker returns an NSDate
object. The NSDate class
is used to store and manipulate what Apple describes as a “single point
in time” (in other words, a date and time).
To access the NSDate represented by a UIDatePicker instance, you’ll make use of the date method. Pretty straightforward, don’t you
think? In our example project, we’ll implement a date picker, and then
retrieve the result, perform some date arithmetic, and display the
results in a custom format.
Picker Views
Picker views (UIPickerView)
are similar in appearance to date pickers but have an almost entirely
different implementation. In a picker view, the only thing that is
defined for you is the overall behavior and general appearance of the
control—the number of components and the content of each component are
entirely up to you. Figure 2 demonstrates a picker view that includes two
components with images and text displayed in their rows.
Unlike other controls, a
picker view’s appearance is not configured in Interface Builder’s
Attributes Inspector or via properties in code. Instead, you need to
make sure you have a class that conforms to two protocols: UIPickerViewDelegate
and UIPickerViewDataSource. I know it’s
been a while, so let’s take a moment for a quick refresher.
Protocols
When I first started
using Objective-C, I found the terminology painful. It seemed that no
matter how easy a concept was to understand, it was surrounded with
language that made it appear harder than it was. A protocol, in my
opinion, is one of these things.
Protocols
define a collection of methods that perform a task. To provide advanced
functionality, some classes, such as UIPickerView, require you to implement methods defined in the protocol.
Some methods are required, others are optional; it just depends on the
features you need.
To make the full use of a UIPickerView, we’ll just add some additional methods to one
of our classes. In our sample application, we’ll be using our view
controller class for this purpose, but in larger projects it may be a
completely separate class—the choice is entirely up to you. A class that
implements a protocol is said to “conform” to that protocol.
We’re going to be using
protocols in the upcoming hours, so it’s important that you get
comfortable with the notion now.
The Picker View Data
Source Protocol
There are two protocols
required by UIPickerView. The first,
the picker view data source protocol (UIPickerViewDataSource), includes methods that describe how much
information the picker will be displaying:
numberOfComponentsInPickerView: Returns the number of components (spinning
segments) needed in the picker.
pickerView:numberOfRowsInComponent: Given a specific component, this method is
required to return the number of rows (different input values) in the
component.
There’s not much to it. As
long as we create these two methods and return a meaningful number from
each, we’ll successfully conform to the picker view data source
protocol. That leaves one protocol, the picker view delegate protocol,
between us and a working picker view.
The Picker View
Delegate Protocol
The delegate protocol (UIPickerViewDelegate) takes care of the real work in creating and
using a picker. It is responsible for passing the appropriate data to
the picker for display and for determining when the user has made a
choice. There are a few protocol methods we’ll use to make the delegate
work the way we want, but again, only two are required:
pickerView:titleForRow:forComponent: Given a row number, this method must return the
title for the row—that is, the string that should be displayed to the
user.
pickerView:didSelectRow:inComponent:
This delegate method will
be called when the user makes a selection in the picker view. The method
will be passed a row number that corresponds to a user’s choice, as
well as the component that the user was last touching.
By
the Way
If you check the documentation for the UIPickerViewDelegate
protocol, you’ll notice that really all the delegate methods are optional—but unless we
implement at least these two, the picker view isn’t going to be able to
display anything or respond to a user’s selection.
As you can see, implementing
protocols isn’t something terribly complicated—it just means that we
need to implement a handful of methods to help a class, in this case a UIPickerView, work the way we want.
To get started with
pickers, we’ll first create a quick date picker example, and then move
on to implementing a custom picker view and its associated protocols.