Xcode 8 / Swift 3 : Simple UIPicker code not working

I have protocols:

class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {

I have data:

let muteForPickerData = ["minute(s)","hour(s)"]

In viewDidLoad I have:

muteForPicker.delegate = self
muteForPicker.dataSource = self

Then I have required methods:

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

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

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

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

Still I get

type viewcontroller does not conform to protocol UIPickerViewDataSource


UIPickerViewDataSource method numberOfComponentsInPickerView is changed in Swift 3 like this that is the reason you are getting this error.

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

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

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

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

}

For more detail read Apple Documentation on UIPickerView.

Note: You need to also add _ as first parameter label same like other methods in your UIPickerViewDelegate method that is titleForRow and didSelectRow.


Details

Xcode 9.2, Swift 4

Fast KIT to use UIPickerView

PickerView

import UIKit

class TextPicker: NSObject {

    var delegate: TextPickerDelegate? {
        didSet {
            textView.inputAccessoryView = delegate?.pickerView(inputAccessoryViewFor: self)
        }
    }

    fileprivate let pickerView = UIPickerView()
    fileprivate var textView = UITextField()
    fileprivate weak var parentViewController: UIViewController?
    public fileprivate(set) var items: [[String]] = []

    init (parentViewController: UIViewController) {
        self.parentViewController = parentViewController
        super.init()
        setupPickerView()
    }

    deinit {
        textView.removeFromSuperview()
    }
}

// MARK: - Getter and Setter

extension TextPicker {

    func set(items: [[String]]) {
        self.items = items
        pickerView.reloadAllComponents()
    }

    var selectedItems: [String] {
        var result = [String]()
        for index in 0..<pickerView.numberOfComponents {
            let selectedRow = pickerView.selectedRow(inComponent: index)
            if index < items.count && selectedRow < items[index].count {
                result.append(items[index][selectedRow])
            }
        }
        return result
    }
}


// MARK: - setup Views

extension TextPicker {

    fileprivate func setupPickerView() {
        pickerView.dataSource = self
        pickerView.delegate = self
        textView.inputView = pickerView
        parentViewController?.view.addSubview(textView)
    }

    func startPicking() {
        textView.becomeFirstResponder()
    }

    func endPicking() {
        textView.resignFirstResponder()
    }
}


// MARK: - UIPickerViewDataSource

extension TextPicker: UIPickerViewDataSource {

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

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

}

// MARK: - UIPickerViewDelegate

extension TextPicker: UIPickerViewDelegate {

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

    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        delegate?.pickerView(didSelect: items[component][row], inRow: row, inComponent: component, delegatedFrom: self)
    }

}

PickerViewDelegate

import UIKit

protocol TextPickerDelegate {

    func pickerView(inputAccessoryViewFor pickerView: TextPicker) -> UIView?
    func pickerView(didSelect value: String, inRow row: Int, inComponent component: Int, delegatedFrom pickerView: TextPicker)

}

Full sample

ViewController

import UIKit

class ViewController: UIViewController {

    fileprivate var picker: TextPicker?
    weak var label: UILabel?

    override func viewDidLoad() {
        super.viewDidLoad()

        let stackView  = UIStackView()
        stackView.axis = .vertical
        stackView.spacing = 16
        view.addSubview(stackView)
        stackView.translatesAutoresizingMaskIntoConstraints = false
        stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true

        let label = UILabel()
        label.text = "..."
        label.textAlignment = .center
        self.label = label
        stackView.addArrangedSubview(label)

        let button = UIButton()
        button.setTitle("Button", for: .normal)
        button.setTitleColor(.blue, for: .normal)
        button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
        stackView.addArrangedSubview(button)

        picker = TextPicker(parentViewController: self)
        picker?.delegate = self
        picker?.set(items: [["aaa", "bbb", "ccc"], ["1", "2", "3"]])
    }

    @objc func buttonTapped() {
        picker?.startPicking()
    }
}

// MARK: PickerViewDelegate

extension ViewController: TextPickerDelegate {

    @objc func pickerCancelAction() {
        picker?.endPicking()
    }

    @objc func pickerSetAction() {
        if let selectedItems = picker?.selectedItems {
            label?.text = "\(selectedItems)"
        }
    }

    func pickerView(inputAccessoryViewFor pickerView: TextPicker) -> UIView? {
        let view = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: 30))
        view.backgroundColor = .white
        let buttonWidth: CGFloat = 100

        let cancelButton = UIButton(frame: CGRect(x: UIScreen.main.bounds.width - buttonWidth - 10, y: 0, width: buttonWidth, height: 30))
        cancelButton.setTitle("Cancel", for: .normal)
        cancelButton.setTitleColor(.black, for: .normal)
        cancelButton.setTitleColor(.lightGray, for: .highlighted)
        cancelButton.addTarget(self, action: #selector(pickerCancelAction), for: .touchUpInside)
        view.addSubview(cancelButton)

        let setButton = UIButton(frame: CGRect(x: 10, y: 0, width: buttonWidth, height: 30))
        setButton.setTitle("Set", for: .normal)
        setButton.setTitleColor(.black, for: .normal)
        setButton.setTitleColor(.lightGray, for: .highlighted)
        setButton.addTarget(self, action: #selector(pickerSetAction), for: .touchUpInside)
        view.addSubview(setButton)

        return view
    }

    func pickerView(didSelect value: String, inRow row: Int, inComponent component: Int, delegatedFrom pickerView: TextPicker) {
        print("\(value)")
    }
}

Result

enter image description here enter image description here