iosswiftuipickerviewuipickerviewdatasourceuipickerviewdelegate

How conform to UIPickerViewDelegate and UIPickerViewDataSource outside of a ViewController?


I want to have multiple UIPickerViews in one scene without bloating my view controller with delegate functions. All the tutorials I've seen on UIPickerViews suggest using the view controller itself as the delegate of a UIPickerView.

How can I move this delegate code out of the ViewController?

Inspired by this tutorial, here is a view controller that assigns itself as the delegate to it's UIPickerViews. I am critical of the if/else logic used to map behavior to each UIPickerView:

import UIKit

class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {

    @IBOutlet weak var pickerViewA: UIPickerView!
    @IBOutlet weak var pickerViewB: UIPickerView!

    let contentsA = ["1A", "2A", "3A"]
    let contentsB = ["1B", "2B", "3B", "4B"]

    override func viewDidLoad() {
        super.viewDidLoad()
        pickerViewA.delegate   = self
        pickerViewA.dataSource = self
        pickerViewB.delegate   = self
        pickerViewB.dataSource = self
    }

    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1 //Applies to both pickerViewA & pickerViewB
    }

    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        if pickerView == pickerViewA {
            return contentsA.count
        } else {
            return contentsB.count
        }
    }

    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        if pickerView == pickerViewA {
            return contentsA[row]
        } else {
            return contentsB[row]
        }
    }

    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        if pickerView == pickerViewA {
            // Do something A
        } else {
            // Do something B
        }
    }
}

Ideally I'd like to have a custom class that conforms to these protocols, and then create a new instance of this class for each UIPickerView so that they have unique delegates & behaviors.

When I attempt to define a separate class called 'PickerDelegate' that conforms to UIPickerViewDelegate & UIPickerViewDataSource, I get this error in Xcode:

Type 'PickerDelegate' does not conform to protocol 'NSObjectProtocol'

Apparently UIPickerViewDelegate & UIPickerViewDataSource inherit from NSObjectProtocol. If I add the protocol stubs (as suggested by Xcode), I get a long list of functions I am not keen on implementing.

Is there an easier way to conform to these protocols outside of a view controller?


Solution

  • When I attempt to define a separate class called 'PickerDelegate' that conforms to UIPickerViewDelegate & UIPickerViewDataSource, I get this error in Xcode:

    Simply make your PickerDelegate a subclass of NSObject.

    class PickerDelegate : NSObject, UIPickerViewDelegate, UIPickerViewDataSource {
    

    This is no imposition, and is necessary, because Cocoa is Objective-C and won't even be able to see your delegate and data source methods otherwise.