I am having difficulties to make an UIPickerView with a done button to appear when the users taps a UITextField. This is my code so far. Everything builds fine, but when I tap the text field, the keyboard appears, not the picker.
class ViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate { @IBOutlet var textField1: UITextField! let pickerData = ["11", "12", "13"] @IBAction func textButton(sender: AnyObject) { let picker: UIPickerView picker = UIPickerView(frame: CGRectMake(0, 200, view.frame.width, 300)) picker.backgroundColor = .whiteColor() picker.showsSelectionIndicator = true picker.delegate = self picker.dataSource = self let toolBar = UIToolbar() toolBar.barStyle = UIBarStyle.Default toolBar.translucent = true toolBar.tintColor = UIColor(red: 76/255, green: 217/255, blue: 100/255, alpha: 1) toolBar.sizeToFit() let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.Plain, target: self, action: "donePicker") let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FlexibleSpace, target: nil, action: nil) let cancelButton = UIBarButtonItem(title: "Cancel", style: UIBarButtonItemStyle.Plain, target: self, action: "donePicker") toolBar.setItems([cancelButton, spaceButton, doneButton], animated: false) toolBar.userInteractionEnabled = true textField1.inputView = picker textField1.inputAccessoryView = toolBar } override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int { return 1 } func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return pickerData.count } func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { return pickerData[row] } func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { textField1.text = pickerData[row] } func donePicker() { textField1.resignFirstResponder() } }
Add this code to your viewDidLoad()
instead of method
let picker: UIPickerView picker = UIPickerView(frame: CGRectMake(0, 200, view.frame.width, 300)) picker.backgroundColor = .whiteColor() picker.showsSelectionIndicator = true picker.delegate = self picker.dataSource = self let toolBar = UIToolbar() toolBar.barStyle = UIBarStyle.default toolBar.isTranslucent = true toolBar.tintColor = UIColor(red: 76/255, green: 217/255, blue: 100/255, alpha: 1) toolBar.sizeToFit() let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItem.Style.done, target: self, action: #selector(self. donePicker)) let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil) let cancelButton = UIBarButtonItem(title: "Cancel", style: UIBarButtonItem.Style.plain, target: self, action: #selector(self. donePicker)) toolBar.setItems([cancelButton, spaceButton, doneButton], animated: false) toolBar.userInteractionEnabled = true textField1.inputView = picker textField1.inputAccessoryView = toolBar
I think it's better to make a custom class to remove code from controller.
Swift 4/5 example:
import Foundation import UIKit protocol ToolbarPickerViewDelegate: class { func didTapDone() func didTapCancel() } class ToolbarPickerView: UIPickerView { public private(set) var toolbar: UIToolbar? public weak var toolbarDelegate: ToolbarPickerViewDelegate? override init(frame: CGRect) { super.init(frame: frame) self.commonInit() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.commonInit() } private func commonInit() { let toolBar = UIToolbar() toolBar.barStyle = UIBarStyle.default toolBar.isTranslucent = true toolBar.tintColor = .black toolBar.sizeToFit() let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(self.doneTapped)) let spaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) let cancelButton = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(self.cancelTapped)) toolBar.setItems([cancelButton, spaceButton, doneButton], animated: false) toolBar.isUserInteractionEnabled = true self.toolbar = toolBar } @objc func doneTapped() { self.toolbarDelegate?.didTapDone() } @objc func cancelTapped() { self.toolbarDelegate?.didTapCancel() } }
Usage example:
class MyViewController: UIViewController { @IBOutlet weak var textField: UITextField! fileprivate let pickerView = ToolbarPickerView() fileprivate let titles = ["0", "1", "2", "3"] override func viewDidLoad() { super.viewDidLoad() self.textField.inputView = self.pickerView self.textField.inputAccessoryView = self.pickerView.toolbar self.pickerView.dataSource = self self.pickerView.delegate = self self.pickerView.toolbarDelegate = self self.pickerView.reloadAllComponents() } } extension MyViewController: UIPickerViewDataSource, UIPickerViewDelegate { func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return self.titles.count } func numberOfComponents(in pickerView: UIPickerView) -> Int { return 1 } func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { return self.titles[row] } func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { self.textField.text = self.titles[row] } } extension MyViewController: ToolbarPickerViewDelegate { func didTapDone() { let row = self.pickerView.selectedRow(inComponent: 0) self.pickerView.selectRow(row, inComponent: 0, animated: false) self.textView.text = self.titles[row] self.textField.resignFirstResponder() } func didTapCancel() { self.textField.text = nil self.textField.resignFirstResponder() } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With