report

A Tutorial on Making a UIPickerView Sample App

Source

An UIPickerView is an iOS Cocoa Touch UIControl in the UIKit framework. The UIPickerView displays one or multiple rows of data in a slot-machine design that a user can select by spinning the list of values. The UIPickerView works in consort with the UIPickerViewDataSource and UIPickerViewDelegate protocols to provide the interaction and to respond to events and also to provide the content to display.


This UIPickerView tutorial will walk you through the steps to implement a UIPickerView in an iPhone or iPad application. The tutorial will demonstrate how to setup the UIPickerView control in Xcode and which required methods are needed to make the control work. Two UIPickerView examples are demonstrated; a simple one column UIPickerView, a two column example which demonstrates how to have multiple columns.

Create Project

For the purposes of this tutorial we will create a iPhone UIPickerView sample application using a single View Controller template. Either from the Xcode File menu or from the main Xcode launch pad select a new Single View project. The following the screenshots provide a visual on the details to setup the initial project. Make sure you select the Storyboard and iPhone as a device in the options.

The Single View project template in Xcode
The Single View project template in Xcode | Source
Xcode project details
Xcode project details | Source
UIPickerView sample app layout in Xcode storyboard
UIPickerView sample app layout in Xcode storyboard | Source

Setup Storyboard

Once the project is created and loaded, open the storyboard and drag a UIPickerView UIControl from the Object Navigator under the Data view section. Also add three UILabels to the storyboard and two UITextFields. One of the UITextFields will display the selected value in the UIPickerView UIControl for the single column example, the second one will display the selected value of the second example. The following screenshot provides a visual on how to the screen should be laid out for this sample app.

The Assistant Editor icon
The Assistant Editor icon | Source

To make connections, hold down the Ctrl key and drag a line with your mouse from the control to the open header file. When you release the mouse button, a popover will appear. Enter the name for the UITextField in the appropriate field and the other option as is. Has it been a button, you would have had to change the connection type to IBAction.

Add Connections, Delegates & Data Source

Open the Assistant editor alongside the storyboard and control drag a connection from the UIPickerView over to the open header file. Name the connection "thePickerView". Also drag a connection from the first UITextField that will serve to display the selected country to the Header file to add another connection and name it selectedCountry.

Repeat the process for the second UITextField. This time name it “selectedCity“. Next you will to create a connection from the UIPickerView to the Proxy (see the adjacent screenshot) of the View Controller. The Proxy is the yellow globe on the bottom of the View Controller in the Storyboard. Drag a connection as before to the Proxy symbol and release the mouse button and select the DataSource in the popover. Repeat the process and select the delegate. This will create the proper entries in the Storyboard source file.

This completes the UI setup, the rest of the work will in done in the ViewController files.

The header file alongside the Storyboard to allow you to make connections
The header file alongside the Storyboard to allow you to make connections | Source

Code Listing 1: The ViewController Header Code

//
//  klViewController.h
//  UiPickerViewControl
//
//  Created by Kevin Languedoc on 9/7/12.
//  Copyright (c) 2012 Kevin Languedoc. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface klViewController : UIViewController<UIPickerViewDataSource,UIPickerViewDelegate>


@property (strong, nonatomic) IBOutlet UIPickerView *thePickerView;
@property (strong, nonatomic) IBOutlet UITextField *selectedCountry;
@property (strong, nonatomic) IBOutlet UITextField *selectedCity;

//property array for one column UIPickerView Example
@property (strong, nonatomic) NSArray *oneColumnList;

//property array for a two column UIpickerCiew Example
@property (strong, nonatomic) NSArray *secondColumnList;

@end

Setup Arrays for UIPickerViewDataSource Data Source

Open the ViewController header file and add the UIPickerViewDelegate and the UIPickerViewDataSource between a pair of angle braces as the following code snippet demonstrates. These are required to add data to the UIPickerView and also be able to react to events from the user.

Each of these protocols have both optional as well as required methods that must be implemented, which will do next.

These are the required methods that must be implemented:

  1. (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
  2. - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
  3. - (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
  4. - (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component


Before you get to these methods, you will first need to synthesize your properties using the @synthesize keyword. This will create the getter-setter properties. You add the the synthesize properties just below the @implementation directive, see code listing 2.

For the single column example, you will need to implement the “oneColumnList” NSArray in the ViewDidLoad method, which is required method of the ViewController and it is added for you when the ViewController is created with the project or when you add a new ViewController. this array will contain the listing for the cities and you will need to use the initWithObjects when you allocate (initialize) the variable. Take a look at code listing 2 below. The code also contains the data for the second example but we will get to that later.

Code Listing 2

//
//  klViewController.m
//  UiPickerViewControl
//
//  Created by Kevin Languedoc on 9/7/12.
//  Copyright (c) 2012 Kevin Languedoc. All rights reserved.
//

#import "klViewController.h"

@interface klViewController ()

@end

@implementation klViewController
@synthesize thePickerView;
@synthesize selectedCountry;
@synthesize selectedCity;
@synthesize oneColumnList;
@synthesize secondColumnList;

- (void)viewDidLoad
{
    thePickerView.showsSelectionIndicator = TRUE;
       
    //One column array example
    self.oneColumnList=[[NSArray alloc] initWithObjects:@"New York",@"Montreal",@"Miami",@"Boston",@"Philadelphia",@"Toronto",@"Calcary",@"Vancouver",@"Los Angeles",@"Cupertino",@"Austin",@"New Orleans", nil];
    
    //Two column array example
    self.secondColumnList=[[NSArray alloc] initWithObjects:@"Canada", @"United States",@"Mexico",@"England",@"France",@"Greece", @"Slovakia",@"Switzerland", nil];
    

    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.
}

- (void)viewDidUnload
{
    [self setThePickerView:nil];
    [self setSelectedCountry:nil];
    [self setSelectedCity:nil];
    [super viewDidUnload];
    // Release any retained subviews of the main view.
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);

Setup Protocol Methods

In this part of the tutorial, you will setup the required methods that I have listed above.

(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component:

This method determines the number of rows to display the UIPickerView. In the above method signature the component argument represent the column. So for a one column UIPickerView the code for this method would look something like the following code in listing 3. All the method does is return the total count of elements in the NSArray oneColumnList.

Code Listing 3

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
       
    return [oneColumnList count];
}

For multiple columns, you would have to tell the UIPickerView control how many elements to return from each array and which component (column) to display them in. The code for this proved in listing 4 below.

Code Listing 4

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
    if (component == 0) {
        return [oneColumnList count];
    }
       
    return [secondColumnList count];
}

As you can see, the components are a 0 based array. The first component, 0, is assigned the number array elements from the first array and the subsequent component is assigned the count of elements of the second array. If you had more columns you would continue to add an elseif statement for each component in the UIPickerView. Of course you also use a switch statement since the component argument is a NSInteger like in code listing 5:

Code Listing 5

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
    switch(component ) {
	case 0:
        		return [oneColumnList count];
		break;
	case 1:
		return [secondColumnList count];
break;
default:
	another array count
    }
       
    }

However this method only determines the number of rows to display in each component. Next you will need to configure the number of components the UIPickerView will have.

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView

This method is quite simple, you only need to return the number of columns your UIPickerView control will have as the following code listing 6:

Code Listing 6

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
    return 1; // or 2 or more
}

Even you rationalize that you are creating an iPad app, so you may be thinking that it is OK to add more columns to the UIPickerView control. Keep in mind that your iPad app will be tested on an iPhone by the Apple App Store review team, because it is one of the conditions for acceptance in the App Store: Your iPad app most run also in an iPhone because a user with an iPhone can buy an iPad app. There is no restrictions.

For the first example, this return value is 1. For the two column example, the value will be 2. You can add as many components as you require. However keep in mind that the user will have to use the finger to turn each spinner, so the more components you have in the UIPickerView, the more narrow those columns will be at runtime, unless you force a horizontal orientation or you have an iPad app.

The next required method to implement will actually display the values of each of the elements in the NSArray in each column(s) and row.

- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger) row
forComponent:(NSInteger)component

This method has an argument called "titleForRow" which will assign each value of the NSArrays to the proper row and component. The forComponent determine in which component to display the value and the row argument keeps track of the row number for you as well. Here are code listing 7 for a single component and a multiple component configuration:

Code Listing 7

- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row
            forComponent:(NSInteger)component
{
    //For one component (column)
    //return [oneColumnList objectAtIndex:row];
    
    //For mutiple columns
    if (component == 0) {
        return [oneColumnList objectAtIndex:row];

    }
    
    return [secondColumnList objectAtIndex:row];

    }

- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component

This final method that must be implemented returns the selected value from the UIPickerView. As the previous methods, this method returns the value based on the component number as the following code listing shows:

Code Listing 8

- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
    if (component == 0) {
        self.selectedCity.text=[oneColumnList objectAtIndex:row];
        return;
    }
    
    self.selectedCountry.text=[secondColumnList objectAtIndex:row];
     
    
}

© 2012 Kevin Languedoc

More by this Author


Comments 15 comments

francesco 4 years ago

Hi! this video is awesome and useful!! it's possible to have the source code?


klanguedoc profile image

klanguedoc 4 years ago from Canada Author

Yes i will send later


klanguedoc profile image

klanguedoc 4 years ago from Canada Author

You can download the source code for this tutorial and other iOS projects from my iOS Dev 101 web site:

https://sites.google.com/site/ios101dev/downloads


Zoey 4 years ago

Thanks for this tutorial. Another very useful post.

I am using a Datepicker instead to update the content of a text field. The text field receives the date selected just fine but I have one small annoying issue.

On my view, I also have 2 other textfields that collect other info from the user using the standard keyboard.

When the user taps any of the 2 text fields, the keyboard pops up. When the user taps the textfield to receive the date, the datepicker pops BUT the cursor shows in the text field!

I have been trying to get rid of it but to no avail. Any help/pointers will be greatly appreciated.

Thanks.


klanguedoc profile image

klanguedoc 4 years ago from Canada Author

Thanks Zoey for nice feedback. You can get rid of the cursor by disabling the UITextField. Select the UITextfield that is the receiver of the date and open the Attribute inspector, scroll down to the Control section, there is a "enabled" attribute that is checked by default. Just remove the checkmark and the field will be read-only from the user's perspective for direct input but will work with UIDatePicker


Zoey 4 years ago

Thanks for your reply.

I have actually tried that approach but it didn't work. When the app 1st loads, the datePicker is visible and the user can set the date as desired.

Next, the user can fill in the other textfields BUT if the user decides to adjust the date, tapping the date textfield again does not respond and so the data picker does not show up.

This is what my code looks like:

#import UIKit/UIKit.h

@interface showDatePickerAgainViewController : UIViewController

@property (strong, nonatomic) IBOutlet UITextField *dateFromPicker;

@property (strong, nonatomic) IBOutlet UITextField *secondTextField;

@property (strong, nonatomic) IBOutlet UIDatePicker *datePicker;

@end

.m file below

#import "showDatePickerAgainViewController.h"

@interface showDatePickerAgainViewController ()

@end

@implementation showDatePickerAgainViewController

@synthesize dateFromPicker = _dateFromPicker;

@synthesize secondTextField = _secondTextField;

@synthesize datePicker = _datePicker;

- (void)viewDidLoad

{

[super viewDidLoad];

// Do any additional setup after loading the view.

//Create and initialize the date picker.

_datePicker = [[UIDatePicker alloc] initWithFrame:CGRectMake(0,250,325,250)];

_datePicker.datePickerMode = UIDatePickerModeDate;

_datePicker.hidden = NO;

_datePicker.date = [NSDate date];

[_datePicker addTarget:self action:@selector(changeDateLabelContent:) forControlEvents:UIControlEventValueChanged];

self.dateFromPicker.inputView = _datePicker;

NSDate *now = [NSDate date];

[_datePicker setDate:now animated:YES];

[self.view addSubview:_datePicker];

}

- (void)viewDidUnload

{

[self setDateFromPicker:nil];

[self setDatePicker:nil];

[self setSecondTextField:nil];

[super viewDidUnload];

}

- (void) changeDateLabelContent:(id)sender{

NSDateFormatter *outputFormatter = [[NSDateFormatter alloc] init];

[outputFormatter setDateFormat:@"MMMM d, yyyy"];

NSString *formatedDate = [outputFormatter stringFromDate:self.datePicker.date];

self.dateFromPicker.text = formatedDate;

self.dateLabel.text = formatedDate;

}

@end

I don't do anything with the 2nd textfield because I just need for the standard keyboard to pop up.

Thanks.


Jim 4 years ago

Thanks Kevin.

Please explain how you would make the second component dependent upon which first component is selected? i.e., if you select Canada, only Canadian cities show up...


klanguedoc profile image

klanguedoc 4 years ago from Canada Author

Zoey,

I will take a look at your code and respond later today. i need to think abou it. sorry for the delay.

Jim

You could try having a two element propertylist of nsdictionary. You would then populate the array that is used by the second component. Then use the NSPredicate to apply a filter to the array using the selected value from the country list. then refresh your UIPickerView.


klanguedoc profile image

klanguedoc 4 years ago from Canada Author

Ok Zoey, I understand what you trying to do. Ok so you need to have the field editable if it is being used as a trigger to display the date picker. Would you consider using a button or label instead to trigger the date picker? Because the cursor will always go into a editable textfield unless you implement a UITextFieldDelegate and write a custom (override) the UITextField. Not an easy answer my friend, but an interesting project. I usually use a UILabel as an IBAction in my apps. I might play around with this idea for a few days to see if I can create something. No promises though :)


klanguedoc profile image

klanguedoc 4 years ago from Canada Author

Zoey, I have an idea but I need to test first. If it works, it will be very simple.


klanguedoc profile image

klanguedoc 4 years ago from Canada Author

Zoey, I have a working solution and it is simple. I am writing up the instructions. i will post a link here later today or tomorrow.


klanguedoc profile image

klanguedoc 4 years ago from Canada Author

Zoey,

Here is the promised solution

http://hubpages.com/technology/Disable-blinking-cu...

You can download the code from my web site at:

https://sites.google.com/site/ios101dev/downloads?...

look for the ModalDatePicker.zip file


Zoey 4 years ago

Awesome! Thanks thanks thanks!


klanguedoc profile image

klanguedoc 4 years ago from Canada Author

You're very welcome


Salar N 2 years ago

Thank You for this great tutorial and the source code. You are a true Canadian for sharing and being kind enough to share your hard work, knowledge and indeed teaching the rest of us noobs. Cheers!

    Sign in or sign up and post using a HubPages Network account.

    0 of 8192 characters used
    Post Comment

    No HTML is allowed in comments, but URLs will be hyperlinked. Comments are not for promoting your articles or other sites.


    Click to Rate This Article

    Menu

    Explore