Tag Archives: UIPickerView

Data Entry with UIPickerView

The keyboard can be a curse. Trying to validate data via keyboard can be a nightmare for many projects. One solution to this problem is using a UIPickerView to limit the values the user can enter. In this lesson, we’ll explore the UIPickerView for numerical input.

UIPickerViews are are series of wheel like scrolling objects. For more on the basics, you can read this article on them. They are made of series of components which contain rows of strings. Picker views use delegates and data sources for most of their functionality instead of actions. More often than not I see people using them totally and completely wrong. Often they are lazy man’s table views or a control exploited to pack more information than a user can handle. What they are good at is giving one pice of data with multiple parts. The digits of a number for example, or Apple’s subclass of UIPickerView UIDatePickerView, which returns a date.

Set Up the Project

For this project you’ll find a started file  advanced-picker-demo-_startwhere I added a label, picker view and a segmented control to a storyboard view controller.

2016-12-12_07-29-51

I gave some specific titles on the segmented control, 99.9, 999.9,59:59.99, and 99x, which I’ll discuss in a moment. I added an outlet for the picker and label, and an action and outlet for the segmented control.

 @IBOutlet weak var displayLabel: UILabel!
 @IBOutlet weak var picker: UIPickerView!
 @IBOutlet weak var segmentedControl: UISegmentedControl!
 @IBAction func segmentedControl(_ sender: UISegmentedControl) {
 }

Again you can find this in the starter file advanced-picker-demo-_start if you wish.

Setting Up a Picker View.

Picker views don’t have actions, but delegates and data sources, just like table views. Adopt the delegate and data source in the class declaration.

class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource

Add two properties to the view controller class. components will hold the components for the UIPickerView and resultString will be the string that the picker view outputs.

var components = [[String]]()
var resultString = ""

The components property is an array of string arrays. The outer array is each digit of the picker, called a component. The inner array is the string title for a given row in the the component. The two data sources tell the picker how many elements are in the outer and inner arrays. For the outer array, Add the following:

 //:MARK - Delegates and data sources
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return components.count
    }
 

For the number of rows in each inner array, add the following:

func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return components[component].count
    }

There’s two  more or less mandatory delegate methods for picker views . One we’ll add now, the other a bit later. Add the following to show the titles in the picker view

func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
    return components[component][row]
}

Adding Component Content

The delegate and data sources you just added are pretty generic. The content to the components property are where the magic happens. Add the following function that returns a string array. We’ll use it to set a component on the picker view.

func numberPickerComponent(from char:Character) -> [String]{
    switch char{
        case "9":
            return ["0","1","2","3","4","5","6","7","8","9"]
        case "5":
            return ["0","1","2","3","4","5"]
        case "x":
            return ["0"," 1/16","1/8","3/16","1/4","5/16","3/8","7/16","1/2","9/16","5/8","11/16","3/4","13/16","7/8","15/16"]
        default:
            return [String(char)]
    }
}

This function returns an array of strings that starts at 0 and goes to the digit indicated. You could make a case for all the numbers between 0 a 9, but I only picked a few for the purposes I have in mind. Most decimal numbers will be 0 through 9. Time functions will need 0 to 5 for minutes and seconds. The x will stand for fractions. I set my fractions to increments of 1/16 to 15/16. The default returns a string of a single character for all other cases.

With these four cases, you can create formats in your picker from a control string. I stuck those control strings in the segmented control, but you could assign them directly of course. A string of 99.9 is a value from 0 to 99.9. A string of 99x is a string of 0 to 99 15/16. While the UIDatePickerView gets times, if you need a TimeInterval for values with less than second, you’ll find it difficult. 59:59.99 give us times from 0 seconds to 59 minutes 59.99 seconds or 3599.99 seconds.

Add the function to take a string and make it into an array of components. This iterates over the string, calls the numberPickerComponent function you just defined to grab an array, and adds that array to the components array. Once assembled, the function returns the completed array.

 func numberPickerComponents(from string:String)->[[String]]{
        
        var components = [[String]]()
        for char in string.characters{
            components += [numberPickerComponent(from:char)]
        }
        return components
    }

Since I’m changing the pickerComponents in a segmented control, on a change of segment, the code updates the picker. It grabs the string for the segment’s titles and makes that the components. Add this:

    @IBAction func segmentedControl(_ sender: UISegmentedControl) {
        let index = sender.selectedSegmentIndex
        let pickerComponentString = segmentedControl.titleForSegment(at: index)!
        components = numberPickerComponents(from: pickerComponentString)
        resetPicker()
            }

This function calls a function we haven’t defined yet, resetPicker. There’s two jobs for the resetPicker function. One is to reload the components of the picker so the correct ones are there. the reloadAllComponenets method of UIPickerView does that. The second can be a bit tricky. The wheels will be inaccurate if you leave the setting from one format to another. I took the simplest approach and zero the components with a loop that selects the first element in each component. The selectRow:inComponenet:Animated: method can animate the change, which I set to true to roll the components back to zero. Add this to your code:

 func resetPicker(){
     picker.reloadAllComponents()
     for index in 0..<components.count{
       picker.selectRow(0, inComponent: index, animated: true)
     }
 }

This code initializes all this in viewDidLoad, setting the delegate and data source to self, and setting the initial segement control, picker view and components to the first segment. Add this to viewDidLoad:

override func viewDidLoad() {
   super.viewDidLoad()
   picker.dataSource = self
   picker.delegate = self
   segmentedControl.selectedSegmentIndex = 0
   let pickerComponentString = segmentedControl.titleForSegment(at: 0)!
   components = numberPickerComponents(from: pickerComponentString)
}

That sets the picker to display correctly. We’d like some output, in this case a string we’ll parse later. The last delegate function is the selection function. I’ll output the selection to the label. Add this to your code where you placed your data sources.

    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        resultString = ""
    
        displayLabel.text = resultString
    }

This is another tricky spot. The parameters of the selection method only gives us the component that changed, not all the components. There’s a picker view method selectedRow(inComponent:) which can get us the index of any component. I loop through all the components and concatenate each to resultString. Add this below resultString:

for index in 0..<components.count{
    let digit = components[index][pickerView.selectedRow(inComponent: index)]
    resultString += digit
}

This almost works. It still has a bug. If using fractions, this code will make 33 1/3 be 331/3. For cases where the component is larger than one character, add an extra space. Add this under the let digit assignment:

if digit.characters.count > 1 {
    resultString += " " 
}

Build and run. The picker view will look like this.

2016-12-12_07-31-26
You can dial a number of 3.1.

2016-12-12_07-32-14
Set the segmented control to 999.99. Dial up 203.14.
2016-12-12_07-33-06

Go to the time of 59:99.00, put a time of 12:54.37 and you get this
2016-12-12_07-34-30
Finally click on 99X for the fractions and try 12 3/8
2016-12-12_07-35-25

You get the values showing up as a string in the title. However you may not want a string for any of these, but a double instead. We’ll need to parse the string into a number. In next week’s lesson, we’ll tackle that part of the picker.

The Whole Code

You can find the finished lesson here for download:advanced-picker-demo.

//
//  ViewController.swift
//  Advanced Picker Demo
//
//  Created by Steven Lipton on 12/12/16.
//  Copyright © 2016 Steven Lipton. All rights reserved.
//

import UIKit

class ViewController: UIViewController, UIPickerViewDelegate,UIPickerViewDataSource {
    
    var components = [[String]]()
    var resultString = ""
    
    
    @IBOutlet weak var displayLabel: UILabel!
    @IBOutlet weak var picker: UIPickerView!
    @IBOutlet weak var segmentedControl: UISegmentedControl!

    @IBAction func segmentedControl(_ sender: UISegmentedControl) {
        let index = sender.selectedSegmentIndex
        let pickerComponentString = segmentedControl.titleForSegment(at: index)!
        components = numberPickerComponents(from: pickerComponentString)
        resetPicker()
    }
    
    func numberPickerComponent(from char:Character) -> [String]{
        switch char{
        case "9":
            return ["0","1","2","3","4","5","6","7","8","9"]
        case "5":
            return ["0","1","2","3","4","5"]
        case "x":
            return ["0"," 1/16","1/8","3/16","1/4","5/16","3/8","7/16","1/2","9/16","5/8","11/16","3/4","13/16","7/8","15/16"]
        default:
            return [String(char)]
        }
    }
    
    func numberPickerComponents(from string:String)->[[String]]{
        
        var components = [[String]]()
        for char in string.characters{
            components += [numberPickerComponent(from:char)]
        }
        return components
    }
    func resetPicker(){
        picker.reloadAllComponents()
        for index in 0..<components.count{ picker.selectRow(0, inComponent: index, animated: true) } } override func viewDidLoad() { super.viewDidLoad() picker.dataSource = self picker.delegate = self segmentedControl.selectedSegmentIndex = 0 let pickerComponentString = segmentedControl.titleForSegment(at: 0)! components = numberPickerComponents(from: pickerComponentString) } //:MARK - Delegates and data sources func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return components.count
    }
    
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return components[component].count
    }
    
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        return components[component][row]
    }
    
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        resultString = ""
        for index in 0..<components.count{ let digit = components[index][pickerView.selectedRow(inComponent: index)] if digit.characters.count > 1 {//add space if more than one character
                resultString += " " //add space if more than one character
            }
            resultString += digit
        }
        displayLabel.text = resultString
    }

}

Converting Objective C to Swift : The Circular Picker View Example

Screenshot 2015-03-01 14.59.10Swift is a new language. Objective C is not. Sometimes you have some Objective C code and want to make it into Swift code. I had a case of that this week when a reader asked me about continuous picker view wheels. If you are familiar with the date picker it goes from 0 to 59 and then loops back to 0, making a continuous wheel. The UIPickerView does not have this ability. When asked, I did not know how to do this. The reader found some code in Objective C that did that. I did a little web searching and I did find a solution. Of course it was in Objective C, not Swift.

I’ve wanted to write a piece on converting from Objective C to Swift for a while. I didn’t have a good example to use. This makes a really good example. We’ll discuss some things you should know and try an example of converting a method for a UIPickerView from Objective C into Swift.

Header Files and Scope

Objective C uses two files for a class, unlike Swift’s single file for a class. A primary reason for this is sharing.  Some data is shared in a class, some isn’t. We refer to how much something shares as its scope. Scope rules are very different between the two languages. One of those two files in Objective C is a header file or .h file. The .h file has information that is public in scope. A public scope allows sharing. Anything private and not shared gets declared in the other file, the  implementation file. Implementation files have  a .m extension. The .h file includes properties, methods target-actions, outlets, and often protocols for delegates. It is in the header file that you find most of the class definition. The header file has no code though: it is only declarations. You will repeat all your methods and target actions in the implementation file when you write the code for it.

In the code I found the header looks like this:

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController<uipickerviewdatasource,uipickerviewdelegate>
@property (weak, nonatomic) IBOutlet UIPickerView *myPicker;
@property  NSInteger maxRows;

@end

In the header file, you will find a series of tags beginning with @. You’ll find on each property a @property tag. These will be the public properties. We have one of these as an outlet to our picker view:

@property (weak, nonatomic) IBOutlet UIPickerView *myPicker;

Let’s dissect this outlet from a Swift perspective. @property (weak, nonatomic) IBOutlet sets an outlet with a weak variable. Since Swift has an even more automatic version of Automatic Reference Counting(ARC) than Objective C, we ignore the nonatomic part. Next we have the class of the identifier, UIPickerView. Finally, we have the identifier for the property *myPicker. the asterisk (*) means  this is a pointer, which is true of most object identifiers in Objective C. Pointers in Objective C can have nil values,. It’s a good idea to their Swift equivalent an optional value.

This translates in Swift to

@IBOutlet weak var myPicker: UIPickerView!

If you never saw the difference between the two languages, you might begin to see why Swift is so speedy for coding. The outlet is merely @IBOutlet. We have a weak variable so next we have weak var. Next comes the identifier myPicker with the class UIPickerView as an optional.
Swift encourages assignment of values to variables and constants at declaration. Objective C does not. In Objective C, you declare in one place and assign in another. Often the assignment happens in the viewDidLoad method of your .m file. When reading Objective C code declarations, keep the viewDidLoad handy. For example our .h file has this line:

  @property  NSInteger maxRows;

in the .m file we have this in viewDidLoad:

self.maxRows = 10;

For all properties, Objective C requires the self identifier. These two lines equal the one line in Swift.:

let maxRows = 10

Which assigns maxRows equal to an Int of 10 and sets the type to Int. Of course this also makes this a constant, which is not the case in Objective C.

There is also an @interface tag in the .h file. Our properties and any public methods are found between this tag and the @end tag.

@interface ViewController : UIViewController<uipickerviewdatasource,uipickerviewdelegate>
…
@end

The @interface tag tells us the identifier of this class, and its superclass. In this app it is UIViewController. In angle brackets after the superclass is a list of adopted protocols. In our case we adopted the picker view data source and delegate. Again Swift simplifies this to essentially one line — and a bracket

class ViewController:UIViewController,UIPickerViewDataSource,UIPickerDelegate{
}

We have two @interface tags in any Objective-C class. The second is in the .m file, and lists private properties. For example we have this:

@interface ViewController ()
    @property NSInteger maxElements;
@end

Swift simplifies this to a mere keyword: private. The viewDidload  method tells us to set this to 10,000. Given that information we get this:

 private let maxElements = 10000

Putting that all together, we can take the header file, plus  the @interface and the viewDidLoad in the implementation file to translate them into Swift:

import UIKit
class ViewController:UIViewController,UIPickerDataSource,UIPickerDelegate{
    let maxRows = 10
    private let maxElements = 10000
    @IBOutlet weak var myPicker:UIPickerView
}

The Meat of the Matter: The Implementation File

Let’s look at the implementation file now and figure out what it is doing. I’ll assume you know about UIPickerView and its delegates.  If you don’t, read this first. The Objective C file is this:

#import "ViewController.h"

@interface ViewController ()
    @property NSInteger maxElements;
@end

@implementation ViewController

#pragma mark delegates and data source

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
    return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
    return self.maxElements;
}

- (NSString *)pickerView:(UIPickerView *)pickerView
             titleForRow:(NSInteger)row
            forComponent:(NSInteger)component{
    NSString *myString;
    NSInteger myRow = row % self.maxRows;
    myString = [NSString stringWithFormat:@"%li",(long)myRow];
    return myString;
}

#pragma mark life cycle
- (void)viewDidLoad {
    [super viewDidLoad];
    self.maxElements = 10000;
    self.maxRows = 10;
    self.myPicker.delegate = self;
    self.myPicker.dataSource = self;
    [self.myPicker selectRow:self.maxElements / 2  inComponent:0 animated:NO];
}

@end

If you have never worked in Objective C before, your biggest challenge is how they put stuff backwards from Swift, C++, Java or most any other language.

Objective C bases its syntax on an older language, Smalltalk. It was a Smalltalk demonstration that inspired Steve Jobs to come up with the Macintosh in the early 1980’s. Both Smalltalk and Objective C look at objects very literally. You state what your object is and then what you want to do with it. You enclose that in square brackets. For example we have

[super viewDidLoad];

which runs the superview’s viewDidLoad. Swift writes that as a function

super.viewDidLoad()

Since this is a picker view, we need to add two data source methods to tell us about our data and one delegate method. Let’s look at one of those methods:

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
    return 1;
}

This implements one of our two required data source methods. We have one component in our picker view, so we return 1. The minus sign at the beginning of the method declaration tells the compiler and developer that this is an instance method. If it was a plus it would be a class method. Next,  the return type is in parentheses, followed by the method identifier. If there are parameters, the parameter list follows. In Swift that looks like this:

func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}

The syntax is very different between the two, but the same information is there. The next data source method works the same, returning a value for the number of rows in the picker, which in this case is a lot.

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
return self.maxElements;
}

We use maxElements to set this size, which we know is 10,000 rows. Why do we need that many rows? The answer lies in the delegate method, titleForRow:

- (NSString *)pickerView:(UIPickerView *)pickerView
             titleForRow:(NSInteger)row
            forComponent:(NSInteger)component{
    NSInteger myRow = row % self.maxRows;
    NSString *myString = [NSString stringWithFormat:@"%li",(long)myRow];
    return myString;
}

The declaration tells us we return a string, and have the parameters of the picker view, the row and the component. The code declares an NSInteger, called myRow which is the modulo of row and maxRows. We take this new value, convert it to a NSString, and then return the string. Unlike properties, in methods we can declare and assign at the same time. However, we need to declare the type when we do.

What does this do? The method takes a massive set of numbers and has a row in that set of 10,000. It takes the remainder of row divided by maxRows. That remainder will repeat between 0 and maxrows - 1.  In effect, the picker repeats between 0 and 9 in our case. If the number for maxElements is big enough, it is unlikely that we will run out of numbers for repetition. Since these are integers, you’ve used a small amount of memory to do this.

You can write the method like this in Swift:

    func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
        let myRow = row % maxRows
        let myString = String(format: "%i", myRow)
        return myString
    }

We returned the string as an optional. When converting from Objective-C to Swift, anything that is a pointer is best converted as an optional.

But we are not done yet. The Objective C code still has the viewDidload method.

- (void)viewDidLoad {
[super viewDidLoad];
self.maxElements = 10000;
self.maxRows = 10;
self.myPicker.delegate = self;
self.myPicker.dataSource = self;
[self.myPicker selectRow:self.maxElements / 2 inComponent:0 animated:NO];
}

The viewDidload method does not need to assign initial values in Swift. That happens when we declare a value. Objective C requires you to explicitly say where something is with self, while Swift is better at guessing. We can get rid of the first two assignments, and get rid of all the self references, though you can leave them there if you wish. The last line of the method makes a call to myPicker and selects a specific row in the picker: 5,000. This way if we roll up or down we will get a repeat. All this in Swift looks like this:

override func viewDidLoad() {
        super.viewDidLoad()
        myPicker.dataSource = self
        myPicker.delegate = self
        myPicker.selectRow(maxElements / 2, inComponent: 0, animated: false)
    }

Our Swift file looks like this now:


import UIKit

class ViewController: UIViewController,UIPickerViewDataSource,UIPickerViewDelegate {
    //MARK: Properties
    let maxRows = 10
    private let maxElements = 10000

    //MARK: Outlets
    @IBOutlet weak var myPicker: UIPickerView!
    func platypus(){
        //doesn't do much

    }
    //MARK: Delegates and DataSources
    func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
        return 1
    }
    func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return maxElements
    }
    func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
        let myRow = row % maxRows
        let myString = String(format: "%i", myRow)
        return myString
    }

    //MARK: Life cycle
    override func viewDidLoad() {
        super.viewDidLoad()
        myPicker.dataSource = self
        myPicker.delegate = self
        myPicker.selectRow(maxElements / 2, inComponent: 0, animated: false)
        platypus()
    }

}

If you’d like to try this code out, make a new single view project. Drag a Picker View  and a label out on to the storyboard. Make it look like this:

Screenshot 2015-03-01 13.56.14

Copy the code above into the ViewController.swift file of your project.

Using the assistant editor, control-drag from the picker to the myPicker outlet. Control-drag from the I want coffee!! label to the Assistant editor. Name the new outlet statusLabel.

Build and run:

 

Screenshot 2015-03-01 14.08.54

 

Using Text Arrays

Not everyone will want the row number. Most likely, you will want something else listed in your picker view. If you want to use text, set up an array like this:

private let coffee = ["Coffee","Latte","Mocha","Machiatto","Frappucino","Espresso","Red Eye","Black Eye","Espresso","Irish Coffee"]

Change the pickerView:titleForRow: to this.

func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
        let myRow = row % coffee.count
        //let myRow = row % maxRows
        //let myString = String(format: "%i", myRow)
        let myString = coffee[myRow]
        return myString
    }

We take the repeating row and use that for the index to the array. Build and run:

 

Screenshot 2015-03-01 14.09.28

 

Returning Values

To get a value from a selected wheel, we use the modulo again. This time in the didSelectRow delegate method. Add this to the code:

func pickerView(pickerView: UIPickerView,didSelectRow row: Int,inComponent component: Int){
        let myRow = row % coffee.count
        let myString = coffee[myRow]
        statusLabel.text = "I love " + myString + "s"
    }

Build and run:

Screenshot 2015-03-02 07.54.18

You can add one more line. For most simple cases it isn’t necessary if you have plenty of space to spin, but there may be times you need to limit your space, or prevent running out of space. You can add this or something like it to didSelectRow:

pickerView.selectRow((maxElements / 2) + row, inComponent: 0, animated:false)

I’d leave maxElements at 10000, and this means maxElements/2 is 5000. Since 5000 a number ending in 0,  we can position the picker back at the middle plus offset of myRow. Whenever the wheel stops, we are back a the middle. Note if maxElements/2 does not divide evenly to 0, you might run into problems and need a little more complicated math.

This is the basics of building a repeating picker view. It also illustrates some of the important issues you need to watch if converting between old Objective-C and Swift. These are not the only issues. Apple has an  e-book on the subject of using Objective-C and Swift, and it is worth the time reading.

The Whole Objective – C Code

//
//  ViewController.h
//  InfinitePicker
//
//  Created by Steven Lipton on 2/28/15.
//  Copyright (c) 2015 MakeAppPie.Com. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController<uipickerviewdatasource,uipickerviewdelegate>
@property (weak, nonatomic) IBOutlet UIPickerView *myPicker;
@property  NSInteger maxRows;

@end
//
//  ViewController.m
//  InfinitePicker
//
//  Created by Steven Lipton on 2/28/15.
//  Copyright (c) 2015 MakeAppPie.Com. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()
    @property NSInteger maxElements;
@end

@implementation ViewController

#pragma mark delegates and data source

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
    return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
    return self.maxElements;
}

- (NSString *)pickerView:(UIPickerView *)pickerView
             titleForRow:(NSInteger)row
            forComponent:(NSInteger)component{
    NSString *myString;
    NSInteger myRow = row % self.maxRows;
    myString = [NSString stringWithFormat:@"%li",(long)myRow];
    return myString;
}

#pragma mark life cycle
- (void)viewDidLoad {
    [super viewDidLoad];
    self.maxElements = 10000;
    self.maxRows = 10;
    self.myPicker.delegate = self;
    self.myPicker.dataSource = self;
    [self.myPicker selectRow:self.maxElements / 2  inComponent:0 animated:NO];
}

@end

The Whole Swift Code

//
//  ViewController.swift
//
//  Created by Steven Lipton on 2/28/15.
//  Copyright (c) 2015 MakeAppPie.Com. All rights reserved.
//

import UIKit

class ViewController: UIViewController,UIPickerViewDataSource,UIPickerViewDelegate {
    //MARK: Properties
    let maxRows = 100
    private let maxElements = 10000
    private let coffee = ["Coffee","Latte","Mocha","Machiatto","Frappucino","Espresso","Red Eye","Black Eye","Espresso","Irish Coffee"]

    //MARK: Outlets
    @IBOutlet weak var myPicker: UIPickerView!
    @IBOutlet weak var statusLabel: UILabel!

    //MARK: Delegates and DataSources
    func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
        return 1
    }
    func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return maxElements
    }
    func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
        //let myRow = row % maxRows
        //let myString = String(format: "%i", myRow)
        let myRow = row % coffee.count
        let myString = coffee[myRow]
        return myString
    }

    func pickerView(pickerView: UIPickerView,didSelectRow row: Int,inComponent component: Int){
        let myRow = row % coffee.count
        let myString = coffee[myRow]
        statusLabel.text = "I love " + myString + "s"
        //if you want to use less elements in maxElements
        // or prevent ever running to the end of the list in your pickerview
        //you can set yourself back to the middle. It is not necessary for most cases.
        pickerView.selectRow((maxElements / 2) + row, inComponent: 0, animated:false)
    }

    //MARK: Life cycle
    override func viewDidLoad() {
        super.viewDidLoad()
        myPicker.dataSource = self
        myPicker.delegate = self
        myPicker.selectRow(maxElements / 2, inComponent: 0, animated: false)
    }

}

Swift Swift: Formatting a UIPickerView

[Updated to Swift 2.0/iOS9.0 9/15/15]

I’ve posted on UIPickerViews before, but in the comments for that post, I got a question that needed an answer so big, it was worth a new post.

UIPickerViews are those spinning wheel slot machine type controls. For a variety of reasons I went into in the earlier post they are not as common as other  controls in iOS. The reason I’ll focus on here is the lack of properties for a UIPickerView. It relies heavily on delegates. In this lesson we’ll look at some of those delegate methods.

Setting Up a Demo

I debated just having people go to the UIPickerView lesson, and start with that, but decided it would be simpler to start with a single-component picker view instead of the double component in that lesson. I will go through the setup quickly and if you  are not understanding something, I’d suggest clicking here and doing that lesson first.

Make a new project with Command-Shift-N, under iOS make a single view project named SwiftUIPickerFormatted. Drag a label and a picker view onto the view controller on the storyboard.
Screenshot 2014-10-20 12.11.21

Open the Assistant editor, and control-drag from the label to make an outlet named myLabel. Control-drag from the picker view and make an outlet named myPicker. Close the assistant editor and go to the ViewController.swift file. Clean up the code, then add the data for the picker view there so it looks like this:

class ViewController: UIViewController {

    @IBOutlet weak var myPicker: UIPickerView!
    @IBOutlet weak var myLabel: UILabel!
    let pickerData = ["Mozzarella","Gorgonzola","Provolone","Brie","Maytag Blue","Sharp Cheddar","Monterrey Jack","Stilton","Gouda","Goat Cheese", "Asiago"]
    override func viewDidLoad() {
        super.viewDidLoad()
        myPicker.dataSource = self
        myPicker.delegate = self
    }

}

UIPickerView needs a delegate and a data source.  In the viewDidLoad above we set both the delegate and dataSource to to self so we can add the required methods here. Change the class declaration to this:

class ViewController: UIViewController,UIPickerViewDataSource,UIPickerViewDelegate {

You will get the classic

Type 'ViewController' does not conform to protocol 'UIPickerViewDataSource'

error.  Xcode is whining at you to add some required methods for the protocol. The two methods are in the data source. Add these towards the bottom of your code:

//MARK: - Delegates and data sources
    //MARK: Data Sources
    func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
        return 1
    }
    func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return pickerData.count
    }

We have only one component for the picker view so we return a literal 1. Using .count we get the number of rows from the data. We have some optional methods to use in UIPickerViewDelegate. Add these below the Data Source methods:

 //MARK: Delegates
    func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        return pickerData[row]
    }

    func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        myLabel.text = pickerData[row]
    }

The first method places the data into the picker and the second selects and displays it in the label. You can now build and run and spin the wheel a bit:
Screenshot 2014-10-21 07.38.20

Formatting Picker Views with Attributed Strings

Now that we have set up a picker view, we might want a different font or color. There is a method which uses attributed strings.  Add this under the code we already have:


    func pickerView(pickerView: UIPickerView, attributedTitleForRow row: Int, forComponent component: Int) -> NSAttributedString? {
        let titleData = pickerData[row]
        let myTitle = NSAttributedString(string: titleData, attributes: [NSFontAttributeName:UIFont(name: "Georgia", size: 15.0)!,NSForegroundColorAttributeName:UIColor.blueColor()])
        return myTitle
    }

If you are not familiar with attributed Strings,  click here and check out my earlier post on how to use them. This should change the font to blue 15 point Georgia given the attributed string we have. Build and run.
Screenshot 2014-10-21 07.39.05
We do get blue, but no font change. You can change many attributes of a string. You cannot change the font in  attributed text  for picker views.

Using UIViews to Format the Picker

There is one more very powerful method for displaying something on a UIPickerView. There is a method in the delegate to display a UIView. One subclass of UIView is a UILabel. So we could make a label and have the delegate method return a formatted UILabel. Add this example using attributed strings to the code we have so far:

    func pickerView(pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusingView view: UIView?) -> UIView {
        let pickerLabel = UILabel()
        let titleData = pickerData[row]
        let myTitle = NSAttributedString(string: titleData, attributes: [NSFontAttributeName:UIFont(name: "Georgia", size: 26.0)!,NSForegroundColorAttributeName:UIColor.blackColor()])
        pickerLabel.attributedText = myTitle
       return pickerLabel
    }

Build and run.

Screenshot 2014-10-21 07.40.24

We get a change of font, but it is left justified. I went back to a black font for our next addition. With a UILabel in this delegate method, you can use all the properties of a UILabel instead of attributed text. I could use text and font property instead of attributed text. I’ll change textAlignment to .Center and I’ll add a colored background to the label by adding this to the delegate method.

         //color  and center the label's background
        let hue = CGFloat(row)/CGFloat(pickerData.count)
        pickerLabel.backgroundColor = UIColor(hue: hue, saturation: 1.0, brightness:1.0, alpha: 1.0)
        pickerLabel.textAlignment = .Center
        return pickerLabel

As I discussed in my color post on hues, this is a simple example to do multiple colors. Line two takes the current element and divides it by the number of elements in our array. Since both are type Int, I cast them to CGFloat before I do the division. This will be a  CGFloat between 0 and 1, which I use as my hue in assigning a color. Build and run:
Screenshot 2014-10-21 07.41.19

You should be a bit more conservative with memory using this. A better way to write this is:

func pickerView(pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusingView view: UIView?) -> UIView {
        var pickerLabel = view as! UILabel!
        if view == nil {  //if no label there yet
            pickerLabel = UILabel()
            //color the label's background
            let hue = CGFloat(row)/CGFloat(pickerData.count)
            pickerLabel.backgroundColor = UIColor(hue: hue, saturation: 1.0, brightness: 1.0, alpha: 1.0)
        }
        let titleData = pickerData[row]
        let myTitle = NSAttributedString(string: titleData, attributes: [NSFontAttributeName:UIFont(name: "Georgia", size: 26.0)!,NSForegroundColorAttributeName:UIColor.blackColor()])
        pickerLabel!.attributedText = myTitle
        pickerLabel!.textAlignment = .Center
        return pickerLabel

    }

This way we check if the label is already created before creating a new one.

Sizing the UIPickerView

You’ll notice that things seem a little squeezed in using the label. One way of fixing this is two more delegate methods: rowHeightForComponent and widthForComponent. Add this to increase the spacing between cells in the picker view:

 func pickerView(pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
        return 36.0
    }

If you build and run, you get this:

Screenshot 2014-10-21 07.42.04

While in a single component, it is not so important, if you have multiple components you can change the width of each spinner as well. Let’s make the component smaller. Add this code:

 func pickerView(pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat {
        return 200
    }

We are making all the components the same here, since we only have one. We could use the component parameter to identify the component and have different lengths for each one. Build and run.

Screenshot 2014-10-21 07.43.00

That is a few of the things you could do with a UIPickerView. There are a lot more, and since it accepts a UIView anything is possible for a user interface. Apple does recommend in the Human Interface Guide that UITableViews are a better option if you want to get complex. But the UIPickerView does have some very good uses, limited by your creativity.

The Whole Code

//
//  ViewController.swift
//  SwiftUIPickerFormatted
//
//  Created by Steven Lipton on 10/20/14.
//  Copyright (c) 2014 MakeAppPie.Com. All rights reserved.
//  Updated to Swift 2.0  9/15/15 SJL

import UIKit

class ViewController: UIViewController,UIPickerViewDataSource,UIPickerViewDelegate {
    
    @IBOutlet weak var myPicker: UIPickerView!
    @IBOutlet weak var myLabel: UILabel!
    let pickerData = ["Mozzarella","Gorgonzola","Provolone","Brie","Maytag Blue","Sharp Cheddar","Monterrey Jack","Stilton","Gouda","Goat Cheese", "Asiago"]
    override func viewDidLoad() {
        super.viewDidLoad()
        myPicker.delegate = self
        myPicker.dataSource = self
        
    }
    //MARK: - Delegates and data sources
    //MARK: Data Sources
    func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
        return 1
    }
    func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return pickerData.count
    }
    //MARK: Delegates
    func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        return pickerData[row]
    }
    
    func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        myLabel.text = pickerData[row]
    }
    
    func pickerView(pickerView: UIPickerView, attributedTitleForRow row: Int, forComponent component: Int) -> NSAttributedString? {
        let titleData = pickerData[row]
        let myTitle = NSAttributedString(string: titleData, attributes: [NSFontAttributeName:UIFont(name: "Georgia", size: 26.0)!,NSForegroundColorAttributeName:UIColor.blueColor()])
        return myTitle
    }
 /* less conservative memory version
    func pickerView(pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusingView view: UIView?) -> UIView {
        let pickerLabel = UILabel()
        let titleData = pickerData[row]
        let myTitle = NSAttributedString(string: titleData, attributes: [NSFontAttributeName:UIFont(name: "Georgia", size: 26.0)!,NSForegroundColorAttributeName:UIColor.blackColor()])
        pickerLabel.attributedText = myTitle
        //color  and center the label's background
        let hue = CGFloat(row)/CGFloat(pickerData.count)
        pickerLabel.backgroundColor = UIColor(hue: hue, saturation: 1.0, brightness:1.0, alpha: 1.0)
        pickerLabel.textAlignment = .Center
        return pickerLabel
    }
  */
    
/* better memory management version */
    func pickerView(pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusingView view: UIView?) -> UIView {
        var pickerLabel = view as! UILabel!
        if view == nil {  //if no label there yet
            pickerLabel = UILabel()
            //color the label's background
            let hue = CGFloat(row)/CGFloat(pickerData.count)
            pickerLabel.backgroundColor = UIColor(hue: hue, saturation: 1.0, brightness: 1.0, alpha: 1.0)
        }
        let titleData = pickerData[row]
        let myTitle = NSAttributedString(string: titleData, attributes: [NSFontAttributeName:UIFont(name: "Georgia", size: 26.0)!,NSForegroundColorAttributeName:UIColor.blackColor()])
        pickerLabel!.attributedText = myTitle
        pickerLabel!.textAlignment = .Center
        
        return pickerLabel
        
    }
    
    func pickerView(pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
        return 36.0
    }
    // for best use with multitasking , dont use a constant here.
    //this is for demonstration purposes only.
    func pickerView(pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat {
        return 200
    }
    
    
    
}

Swift Swift: Implementing Picker Views

[Updated to Swift3.0 10/4/16 SJL]

Screenshot 2014-09-17 06.45.53
Example of A UiPickerView In action

This week, we’ll look at UIPickerView. Picker views are the spin-wheel like controls that often remind me of slot machines. Picker views, like table and collection views, are delegate based. You need to adopt a delegate and data source to get them to work properly. Picker views also take a lot of screen real estate that is not under developer control, which means your view can get crushed quickly. These two probably account for its lack of popularity. Most of what can be done in a picker view can be done far better in a table view. For those three reasons the fourth reason is also true: Nothing yells “newbie” like over-using picker views.

Professional developers do not use them as often as table views, but they work well for static multi-selection situations. When you have a few items to select from that will never change a picker view works well. We’ll set up a pizza order with a picker view today.

Setting Up the Basic Layout

Start with a new Swift single-view project in Xcode named SwiftPickerViewPizzaDemo. Go into the storyboard and make the following layout with two labels and a picker view:

Screenshot 2014-09-17 06.47.04

You can also download the starter file swiftpickerviewpizzademo_star.t.zip which has the layout done for you.

We’ll need outlets for both the picker and the label. Open the assistant editor and control drag the lower label and the picker to the code. Name them myLabel and myPicker. I’m also adding //MARK comments to my code to keep things organized. //MARK is the objective-C equivalent of #pragma mark. After cleaning up your code, it should look like this:

class ViewController: UIViewController {
//MARK -Outlets and Properties
@IBOutlet weak var myLabel: UILabel!
@IBOutlet weak var myPicker: UIPickerView!

//MARK - Instance Methods

//MARK - Life Cycle
override func viewDidLoad() {
super.viewDidLoad()

}
//MARK - Delgates and Data Source
}

Add Our Picker Data

The UIPickerView uses an array of arrays to store the titles for the wheels. Each wheel is called a component. Each component gets it own array. I want to use both size and topping information so add the following just above the Outlets mark.

let pickerData = [
    ["10\"","14\"","18\"","24\""],
    ["Cheese","Pepperoni","Sausage","Veggie","BBQ Chicken"]
]

Adopt the Delegates and Data Source

Now we adopt the protocols UIPickerViewDataSource and UIPickerViewDelegate by listing them in the class declaration

class ViewController: UIViewController,UIPickerViewDataSource,UIPickerViewDelegate {

Xcode will give us an error about required methods we need to implement. Ignore it for now. Go down to viewDidLoad and make sure we assign ourselves to the delegate:

//MARK - Life Cycle
override func viewDidLoad() {
    super.viewDidLoad()
    myPicker.delegate = self
    myPicker.dataSource = self
}

Implement the Picker Data Source Methods

Xcode is still complaining

Type 'ViewController' does not conform to protocol 'UIPickerViewDataSource'

Let’s get rid of this.  Add the data source required methods. These methods tell the picker the size of the array we set up in pickerData. There are two methods. Add this below the Delegate and Data Source mark.

func numberOfComponents(in pickerView: UIPickerView) -> Int {
return pickerData.count
}

Line 2 specifies the number of components in the pickerData array in the method. The second method looks at each component and gives the number of rows in the component:

func pickerView(_ 
    pickerView: UIPickerView,
    numberOfRowsInComponent component: Int
) -> Int {
    return pickerData[component].count
}

The method give us the specified component in component. We access that element of the array, which is another array. We use the count property on the second array, and return that value.

Implement the Required Delegate

Anyone who has ever used a UITableView may find this vaguely familiar. In UITableView we use sections and rows instead of components and rows of UIPickerView, but it is the same two required methods. There is a presenting method similar to a table view’s cellForRowAtIndexPath in a picker’s delegate:

func pickerView(_
    pickerView: UIPickerView,
    titleForRow row: Int,
    forComponent component: Int
) -> String? {
    return pickerData[component][row]
}

Instead of a cell like its table view equivalent, the method returns the title string that goes in that row and component of the spinner.

Selecting a Row

We set up the required methods for the protocol. We have one more step. We need to get the selection. Add this to the delegate section of your code:

func pickerView(
    pickerView: UIPickerView,
    didSelectRow row: Int,
    inComponent component: Int)
{
    updateLabel()
}

In our case all we want to do is to print out the results of the selection. We’ll need that twice once here and once in initialization. A method might be a good idea then. Add a method to update the label.

//MARK - Instance Methods
func updateLabel(){
let size = pickerData[0][myPicker.selectedRowInComponent(0)]
let topping = pickerData[1][myPicker.selectedRowInComponent(1)]
pizzaLabel.text = "Pizza: " + size + " " + topping
}

Line 2 and 3 use the selectedRowInComponent method to access the selected array row.

Build and run. We start with this:

Screenshot 2014-09-18 05.40.01

Dial an 18″ sausage pizza and you get this:

Screenshot 2014-09-18 05.39.49

Selection and Cleaning up

While I wouldn’t call it a bug, there are three issues with his code I don’t like. It’s bad coding and documentation to use literal numbers for component. What is better is to use constants. Add this under the pickerData declaration:

let sizeComponent = 0
let toppingComponent = 1

In this enum, we make the values literal integers that represent the components. This way if we change around our components while using them in multiple methods, we can control things from the one enum. In Swift, the enumerations will need a little help though to get the value out. For a enum to give its raw value use the rawValue property. Change updateLabel to this:

 func updateLabel(){
      
        let size = pickerData[sizeComponent][myPicker.selectedRow(inComponent:sizeComponent)]
        let topping = pickerData[toppingComponent][myPicker.selectedRow(inComponent:toppingComponent)]
        myLabel.text = "Pizza: " + size + " " + topping
    }

While longer, this is much more readable and flexible code.

When we start, the picker  does not reflect in the label. I’d like that to happen. I also want to start with a 18″ pizza, since a large pizza is the most ordered size. In viewDidLoad, add the following code:

myPicker.selectRow(2,
    inComponent: sizeComponent,
    animated: false)
updateLabel()

Line 2 uses another UIPickerView method selectRow to set the size component to 18″. I added the updateLabel method after this. What shows on the picker at initialization shows up in the label.
Build and run, and we get the behavior we want.

Making the UI Pretty With Translucency.

That is all the code for making a UIPickerView, however the storyboard still doesn’t look like much. I’m going to do this with Auto Layout off, but you can certainly leave it on. If you want it off, go to the file inspector and turn it off. For the background I used this graphic:

Photo Sep 14, 7 40 59 PM_small
Click the thumbnail to get the full size image.

Click the image above and when you have the full size one, right-click or save the image. Drag the saved file into your project file list. Make sure copy item is checked in the dialog box. Once you add the image to your project, in the lower right of the utilities pane you will find a little film clip icon, which is the media library. Click the film clip icon, and then drag the pizza image onto the storyboard view. It will cover everything else.  Look at the document outline. If it is not visible click the small icon on the lower left of the storyboard.  You should see this:

The bottom of the outline is the top of the view stack.
The bottom of the outline is the top of the view stack.

Drag the photo to the top of the outline in the view like this, so it is at the bottom of the view stack:

Screenshot 2014-09-18 06.44.44

Now we can see the controls — sort of. We need a background behind the labels, and need to see through the picker.

Screenshot 2014-09-18 06.44.53

Change the background of the picker controller to a white with a 50% alpha. I use the crayon mode and use Snow at 50%

Screenshot 2014-09-18 06.45.27

We’ll make a background layer for the two labels. Drag out  a view (not a view controller) onto the bottom of the photo and size it so it fits across the view. Set the background  on the blank view once again to Snow at 50% alpha.

Screenshot 2014-09-18 06.47.37

Drag the Labels into the view on the bottom and place the labels in the right spots. When you drop a view into another view it becomes a subview of that view.

Screenshot 2014-09-18 06.49.50

Drag the view, not the labels, up above the picker.  Everything moves up to the new place.

Screenshot 2014-09-18 06.50.34

Build and Run:

Screenshot 2014-09-18 06.52.13

You have a better looking UI.

Picker Views: Fastest Way to a One Star Review

Picker views have one purpose: When you have a few items which never change to pick from. For the demo above with a few sizes and pizza toppings, they work fine. For more than 15 items per component, use a table view. For more than three components, these get ugly since there is not enough space. Picker views above these limits are bad user experiences. They take too long to select items quickly for users. They are not good at dynamic selection, and although there is some formatting you can do, they have severe limitations in formatting. My suggestion is for most places above the limits I just mentioned, use a table view. Nothing says you don’t know what you are doing quite like over using picker views. The few who buy your app will give you a one-star review, Everyone else will stay away.

The Whole Code:

You can find the finished project, where I did use auto layout, here:  swiftpickerviewpizzademo below you can find the code.

//
//  ViewController.swift
//  SwiftPickerViewPizzaDemo
//
//  Created by Steven Lipton on 10/4/16.
//  An update of the earlier  9/18/14 article to Swift 3.0
//  Copyright © 2016 Steven Lipton. All rights reserved.
//

import UIKit

class ViewController: UIViewController,UIPickerViewDataSource,UIPickerViewDelegate {

    @IBOutlet weak var myPicker: UIPickerView!
    @IBOutlet weak var pizzaLabel: UILabel!
    let pickerData = [
        ["10\"","14\"","18\"","24\""],
        ["Cheese","Pepperoni","Sausage","Veggie","BBQ Chicken"]
    ]
    
    let sizeComponent = 0
    let toppingComponent = 1
   
    
    
    //MARK: - Picker View Data Sources and Delegates
    
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return pickerData.count
    }
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
         return pickerData[component].count
    }
    
    func pickerView(_
        pickerView: UIPickerView,
                    titleForRow row: Int,
                    forComponent component: Int
        ) -> String? {
        return pickerData[component][row]
    }
    
    func pickerView(_
        pickerView: UIPickerView,
        didSelectRow row: Int,
        inComponent component: Int)
    {
        updateLabel()
    }
    
    //MARK: - Instance Methods
    func updateLabel(){
        let size = pickerData[sizeComponent][myPicker.selectedRow(inComponent: sizeComponent)]
        let topping = pickerData[toppingComponent][myPicker.selectedRow(inComponent: toppingComponent)]
        pizzaLabel.text = "Pizza: " + size + " " + topping
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        myPicker.delegate = self
        myPicker.dataSource = self
        myPicker.selectRow(2, inComponent:sizeComponent, animated: false)
        updateLabel()
    }
    
    
}