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