Custom iOS UIPickerView

Filed Under: iOS

In this tutorial, we’ll be customising the UIPickerView properties in our iOS Application. In the previous tutorial, we implemented the UIPickerView class and discussed some of the important helper properties and functions.

UIPickerView

We know that UIPickerView requires the two protocols: UIPickerViewDataSource, UIPickerViewDelegate.

Besides the required methods that we had discussed, we can use the following methods to customize the UI of the UIPickerView.


func pickerView(_ pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat
func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat
func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView

Using the above three methods we can override the width and height of the cell, and the view of each cell.

Inside the viewForRow method, we can customize the UILabel by creating our own or just create any random custom view such as a UIImage + UILabel.

To change the background color of the UIPickerView simply use the backgroundColor property over the instance.

In the following section, we’ll first create a UIPickerView with a custom label. Later we’ll add a custom view in place of the custom label.

Project Storyboard

ios custom uipickerview

We’ve added two UITextField and connected them in the ViewController.swift file.

Code

The code for the ViewController.swift file is given below:


import UIKit

class ViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {
    
    
    @IBOutlet weak var textField1: UITextField!
    @IBOutlet weak var textField2: UITextField!
    let picker1 = UIPickerView()
    var arrayOfCountries = ["India","USA","Germany","China", "France","Japan", "Australia", "Greece"]
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        createPickerView()
        createToolbar()
    }
    
    func createPickerView()
    {
        picker1.delegate = self
        picker1.delegate?.pickerView?(picker1, didSelectRow: 0, inComponent: 0)
        textField1.inputView = picker1
        textField2.inputView = picker1
        picker1.backgroundColor = UIColor.brown
        
    }
  

    
    func createToolbar()
    {
        let toolbar = UIToolbar()
        toolbar.sizeToFit()
        toolbar.tintColor = UIColor.red
        toolbar.backgroundColor = UIColor.blue
        let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(ViewController.closePickerView))
        toolbar.setItems([doneButton], animated: false)
        toolbar.isUserInteractionEnabled = true
        textField1.inputAccessoryView = toolbar
        textField2.inputAccessoryView = toolbar
    }
    
    @objc func closePickerView()
    {
        view.endEditing(true)
    }

    
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }
    
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        
        return arrayOfCountries.count
    }
    
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String?
    {
        return arrayOfCountries[row]
    }
    
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        
        textField1.text =  arrayOfCountries[row]
    }
    
    func pickerView(_ pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat {
        return 100.0
    }
    
    func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
        return 60.0
    }

    func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {

        var label:UILabel
        
        if let v = view as? UILabel{
            label = v
        }
        else{
            label = UILabel()
        }
        
        label.textColor = UIColor.yellow
        label.textAlignment = .left
        label.font = UIFont(name: "Helvetica", size: 16)
        
        label.text = arrayOfCountries[row]
        
        return label
    }
}

In the viewForRow method, we have set the UILabel color and a system font.
We must update the text here.

UIToolbar Tint color is set on the Buttons present in the Toolbar.

The output when the above application was run on a simulator is:

ios custom uipickerview output

In the next section, we’ll create a Dynamic UIPickerView on the second UITextField. We will show a UIImage from the assets in the custom rows.

UIPickerView Row with UIImage

The code for the updated ViewController.swift file is given below;


import UIKit

class ViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate, UITextFieldDelegate {
    
    
    @IBOutlet weak var textField1: UITextField!
    @IBOutlet weak var textField2: UITextField!
    let picker1 = UIPickerView()
    var arrayOfCountries = ["India","USA","Germany","China", "France","Japan", "Australia", "Greece"]
    var arrayOfColors = ["Red","Orange","Yellow","Green", "Blue","Black"]
    var activeTextField = 0
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        textField1.delegate = self
        textField2.delegate = self
        createPickerView()
        createToolbar()
    }
    
    func createPickerView()
    {
        picker1.delegate = self
        picker1.delegate?.pickerView?(picker1, didSelectRow: 0, inComponent: 0)
        textField1.inputView = picker1
        textField2.inputView = picker1
        picker1.backgroundColor = UIColor.brown
    }
  

    
    func createToolbar()
    {
        let toolbar = UIToolbar()
        toolbar.sizeToFit()
        toolbar.tintColor = UIColor.red
        toolbar.backgroundColor = UIColor.blue
        let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(ViewController.closePickerView))
        toolbar.setItems([doneButton], animated: false)
        toolbar.isUserInteractionEnabled = true
        textField1.inputAccessoryView = toolbar
        textField2.inputAccessoryView = toolbar
    }
    
    @objc func closePickerView()
    {
        view.endEditing(true)
    }

    
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }
    
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        
        switch activeTextField
        {
        case 1:
            return arrayOfCountries.count
        case 2:
            return arrayOfColors.count
        default:
            return arrayOfColors.count
        
        }
    }
    
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String?
    {
        switch activeTextField{
        case 1:
            return arrayOfCountries[row]
        case 2:
            return arrayOfColors[row]
        default:
            return arrayOfColors[row]
        }

    }
    
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        switch activeTextField{
        
        case 1:
            textField1.text =  arrayOfCountries[row]
            break
            
        case 2:
            textField2.text = arrayOfColors[row]
            break

        default:
            textField1.text =  arrayOfCountries[row]
            break
            
        }
    }
    
    func pickerView(_ pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat {
        return 100.0
    }
    
    func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
        return 60.0
    }

    func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
        
        
        switch activeTextField{
        case 1:
        var label:UILabel
        
        if let v = view as? UILabel{
            label = v
        }
        else{
            label = UILabel()
        }
        
        label.textColor = UIColor.yellow
        label.textAlignment = .left
        label.font = UIFont(name: "Helvetica", size: 16)
        
        label.text = arrayOfCountries[row]
        
        return label
         
        case 2:
            
            let parentView = UIView()
            let label = UILabel(frame: CGRect(x: 60, y: 0, width: 80, height: 50))
            let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 50, height:50))
            imageView.image = UIImage(named: "ic_launcher")
            label.text = arrayOfColors[row]
            parentView.addSubview(label)
            parentView.addSubview(imageView)
            
            return parentView
            
        default:
            return UILabel()
            
        }
    }
    
    func textFieldDidBeginEditing(_ textField: UITextField) {
        
        switch textField {
        case textField1:
            activeTextField = 1
            picker1.reloadAllComponents()
        case textField2:
            activeTextField = 2
            picker1.reloadAllComponents()
        default:
            activeTextField = 0
        }
        
    }
}

In the above code, we’ve also added TextFieldDelegate Procol in order to detect which UITextField is focused. Based on that we display the relevant UIPickerView with the respective data.

In the textFieldDidBeginEditing method, we set the activeField Property to 1 or 2 based on the UITextField that is focused.

After that we update the UIPickerView by calling reloadAllComponents()

The output of the above application in action is given below

ios custom uipickerview output 2

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

Comments

  1. Erik says:

    In the function:

    pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView

    You are setting up the label in either case, and not reusing it if it’s already available. I’m not sure how to reuse a custom view, but in case 1 where you have a UILabel, you are checking if it exists, but not returning it immediately. Instead, you are setting it up whether it’s available or not. This means every time you scroll the UIPicker view it will assign those properties, which seems unnecessary, unless I’m mistaken.

    1. ios Dev says:

      {
      var whiteLabel = view as? CustomLabel

      if whiteLabel == nil {
      whiteLabel = CustomLabel()
      }
      whiteLabel!.text = allCapsules[row].title
      whiteLabel!.backgroundColor = UIColor(hex: pets[row].color!)
      return whiteLabel!
      }

      As you can see that’s how you reuse a view/customView

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