iOS UIPickerView

Filed Under: iOS

In this tutorial, we’ll be discussing UIPickerView component in our iOS Application.

UIPickerView

UIPickerView is a UI element which is used to make a selection from multiple choices displayed in a vertical wheel. It is quite similar to what a dropdown is in Android or Web UI.

A wheel is known as a component in a UIPickerView.
A single UIPickerView can host one or more components.
Every component can contain independent data and can be selected and changed.
Typically, an array is used to display data in the rows of a UIPickerView component.

To show data in the UIPickerView you must adopt the protocol, UIPickerViewDataSource in your application and implement its required methods.
You must adopt the protocol, UIPickerViewDelegate in order to display the rows and also to allow making user selections.

If you don’t use the latter protocol in your ViewController, you might end up with something like this :

ios uipickerview wrong

Following are the four most important functions:


func numberOfComponents(in pickerView: UIPickerView) -> Int
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int 
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String?
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int)
  • titleForRow is used to display the data in the UIPickerView component(s).
  • didSelectRow gets called whenever any of the UIPickerView rows is selected.

Before we get down to the complex stuff in a later section, let’s implement a simple UIPicker in our XCode Single View Application Project.

Project Storyboard

ios uipickerview storyboard

We’ve added a UIPickerView, a UILabel and a UITextField. We’ll display the selection from the UIPickerView in the UILabel. Later we’ll create another UIPickerView at the bottom to choose the text for the UITextField.

Code

The code for the ViewController.swift is given below:


import UIKit

class ViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {
    

    @IBOutlet weak var myTextField: UITextField!
    @IBOutlet weak var myLabel: UILabel!
    @IBOutlet weak var topPickerView: UIPickerView!
    
    var arrayOf100 = Array(0...100)
    var arrayOf2 = Array(1...2)
    
    var labelString = ""
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        topPickerView.delegate = self
        
        topPickerView.delegate?.pickerView?(topPickerView, didSelectRow: 0, inComponent: 0)
        topPickerView.delegate?.pickerView?(topPickerView, didSelectRow: 0, inComponent: 1)
        
    }
    
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 2
    }
    
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        if component == 0{
        return arrayOf2.count
        }
        else{
            return arrayOf100.count
        }
    }
    
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String?
    {
        if component == 0 {
            return String(arrayOf2[row])
        }
        else{
            return String(arrayOf100[row])
        }
    }
    
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        
        let digitsValue = arrayOf2[topPickerView.selectedRow(inComponent: 0)]
        let decimalValue = arrayOf100[topPickerView.selectedRow(inComponent: 1)]
        
        myLabel.text = "Value is \(digitsValue).\(decimalValue)"
        
        if component == 0 {
            print(String(arrayOf2[row]))
        }
        else{
            print(String(arrayOf100[row]))
        }
    }
}

topPickerView.delegate = self is the most important line. In a way it activates the Protocols on the UIPickerView instance.

We’re returning 2 components and we’ve populated each of them with a different array.

didSelectRow gets called whenever any of the rows are selected. BUT, it won’t call all of the components in the selection. For that, we use the selectedRow(inComponent:) to get the values from the selected rows of the components. These are then appended and displayed in a string.

Since the initial state doesn’t trigger didSelectRow, we call the method on both of our components in the viewDidLoad method.

The output when the above application was run on the iOS Simulator is:

ios uipickerview output 1

Now let’s create another UIPickerView which would open instead of the Keyboard.

UIPickerView in place of Keyboard

To add a UIPickerView in place of keyboard we just need to set the inputView property on the UITextField to the UIPickerView.


func createAnotherPicker()
    {
        let anotherPicker = UIPickerView()
        anotherPicker.delegate = self
        anotherPicker.delegate?.pickerView?(anotherPicker, didSelectRow: 0, inComponent: 0)
        myTextField.inputView = anotherPicker
    }

Following is the updated code of the ViewController.swift. All we need to do in the delegate methods is to check for the type of the UIPickerView instance in order to use both the UIPickerViews


import UIKit

class ViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {
    

    @IBOutlet weak var myTextField: UITextField!
    @IBOutlet weak var myLabel: UILabel!
    @IBOutlet weak var topPickerView: UIPickerView!
    
    var arrayOf100 = Array(0...100)
    var arrayOf2 = Array(1...2)
    
    var labelString = ""
    let anotherPicker = UIPickerView()
    
    var arrayOfCountries = ["India","USA","Germany","China", "France","Japan", "Australia", "Greece"]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        topPickerView.delegate = self
        
        topPickerView.delegate?.pickerView?(topPickerView, didSelectRow: 0, inComponent: 0)
        topPickerView.delegate?.pickerView?(topPickerView, didSelectRow: 0, inComponent: 1)
        createAnotherPicker()
        
        
    }
    
    func createAnotherPicker()
    {
        anotherPicker.delegate = self
        anotherPicker.delegate?.pickerView?(anotherPicker, didSelectRow: 0, inComponent: 0)
        myTextField.inputView = anotherPicker
    }
    
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        
        if pickerView == topPickerView{
        return 2
        }
        else{
            return 1
        }
    }
    
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        
        if pickerView == topPickerView{
            if component == 0{
            return arrayOf2.count
            }
            else{
                return arrayOf100.count
            }
        }
        else{
            return arrayOfCountries.count
        }
    }
    
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String?
    {
        if pickerView == topPickerView{
            if component == 0 {
                return String(arrayOf2[row])
            }
            else{
                return String(arrayOf100[row])
            }
        }
        else{
            return arrayOfCountries[row]
        }
    }
    
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        
        if pickerView == topPickerView{
            let digitsValue = arrayOf2[topPickerView.selectedRow(inComponent: 0)]
            let decimalValue = arrayOf100[topPickerView.selectedRow(inComponent: 1)]
            
            myLabel.text = "Value is \(digitsValue).\(decimalValue)"
            
            if component == 0 {
                print(String(arrayOf2[row]))
            }
            else{
                print(String(arrayOf100[row]))
            }
        }
        else{
            myTextField.text =  arrayOfCountries[row]
        }
    }
}

The output of the above-updated application in action is :

ios uipickerview output 2

In order to dismiss the UIPickerView, we can set the touchesBegan method with view.endEditing(true).

An even better option is to add a UIToolbar with a dismiss button on the UIPickerView.

UIPickerView with UIToolbar

Add the following function in your ViewController.swift file


func createToolbar()
    {
        let toolbar = UIToolbar()
        toolbar.sizeToFit()
        let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(ViewController.closePickerView))
        toolbar.setItems([doneButton], animated: false)
        toolbar.isUserInteractionEnabled = true
        myTextField.inputAccessoryView = toolbar
    }

We’ve added a Button in the UIToolBar and set the Toolbar as the inputAccessoryView. That means it would be shown above the UIPickerView when the UITextField is clicked.

When the Done button is clicked, the following method would get executed:


@objc func closePickerView()
    {
        view.endEditing(true)
    }

This will close the input view.

Don’t forget to add the method createToolbar() in your viewDidLoad method.

The output of the application in action is:

ios uipickerview output 3

This brings an end to this tutorial. You can download the project from the link below:

Comments

  1. Sandeep Singh says:

    Hi Anupam, Thanks for such a nice tutorial. Keep it up it will help for beginners like me to understand.

  2. Misha Muraly says:

    How to loop the contents of UIPickerView???
    Means after last value show first value again

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages