Xcode 8/Swift 3: простой код UIPicker не работает

У меня есть протоколы:

class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {

У меня есть данные:

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

В viewDidLoad у меня есть:

muteForPicker.delegate = self
muteForPicker.dataSource = self

Тогда у меня есть необходимые методы:

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) {
    }

Тем не менее я получаю

тип viewcontroller не соответствует протоколу UIPickerViewDataSource

Ответ 1

UIPickerViewDataSource метод numberOfComponentsInPickerView изменен в Swift 3 таким образом, что является причиной, по которой вы получаете эту ошибку.

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) {

}

Подробнее читайте Документация Apple на UIPickerView.

Примечание.. Вы также должны добавить _ в качестве метки первого параметра, как и другие методы в вашем методе UIPickerViewDelegate, который равен titleForRow и didSelectRow.

Ответ 2

Детали

Xcode 9.2, Swift 4

Быстрый KIT для использования 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)

}

Полный образец

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)")
    }
}

результат

enter image description here enter image description here