27

One short question: on a registration process I would like to ask the user to choose a value from a list of values.

Is it the right way to use a view Controller adding there all text fields and for the values a picker view? As the picker view needs so much space in between the text fields area I wonder what the best practice in this case would be?

this is my code so far:

class RegisterViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource{

@IBOutlet weak var gradeTextField: UITextField!
@IBOutlet weak var gradePicker: UIPickerView!

let gradePickerValues = ["5. Klasse", "6. Klasse", "7. Klasse"]

func numberOfComponentsInPickerView(pickerView: UIPickerView!) -> Int{
    return 1
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int{
    return gradePickerValues.count
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
    return gradePickerValues[row]
}
func pickerView(pickerView: UIPickerView!, didSelectRow row: Int, inComponent component: Int){
    gradeTextField.text = gradePickerValues[row]
    self.view.endEditing(true)
}

override func viewDidLoad() {
    super.viewDidLoad()

    statusMessageLabel.hidden = true

    gradePicker.dataSource = self
    gradePicker.delegate = self
    gradePicker.hidden = true

    gradeTextField.inputView = UIPickerView()
    gradeTextField.text = gradePickerValues[0]

}

The pickerview is hidden at the beginning and appears only when I select the text field, this is fine so far... But the the picker view is empty...

controller view with open picker view

Johannes Fahrenkrug
  • 42,912
  • 19
  • 126
  • 165
user1555112
  • 1,897
  • 6
  • 24
  • 43

2 Answers2

41

It depends on controller appearance. If there only one choose action per screen it will be better to put Table View on it and selected row will be current selection.

If screen has multiply fields, that user should act with, then, in my opinion, it's better to put label + button above it and when user press this button you just shows Picker View from screen bottom. When user select any row in Picker View you change label text, but don't hide picker itself, it should be done by pressing "Done" button you place above.

Hope this helps.


Update:

Your problem because you just forget to set dataSource property of UIPickerView

Just do: gradePicker.dataSource = self in viewDidLoad()

And don't forget to implements protocol here: class RegisterViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource


Update 2:

Finally made it. If you add UIPickerView in inputView of your textFiled, then It should NOT be in IB. So you could remove it from storyboard (or .xib, if you use it).

Then change code to be something like this:

class RegisterViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {

    @IBOutlet weak var gradeTextField: UITextField!
    var gradePicker: UIPickerView!

    let gradePickerValues = ["5. Klasse", "6. Klasse", "7. Klasse"]

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

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

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

    func pickerView(pickerView: UIPickerView!, didSelectRow row: Int, inComponent component: Int){
        gradeTextField.text = gradePickerValues[row]
        self.view.endEditing(true)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        gradePicker = UIPickerView()

        gradePicker.dataSource = self
        gradePicker.delegate = self

        gradeTextField.inputView = gradePicker
        gradeTextField.text = gradePickerValues[0]
    }
}
Shades
  • 5,568
  • 7
  • 30
  • 48
Alexander Zimin
  • 1,700
  • 1
  • 15
  • 19
  • I just noticed, that I have an error in my code, as the picker view was always displayed inbetween the fields... Now it gets open on from the screen button, but I still have the error that its empty... I will update my initial post with code details. – user1555112 Feb 20 '15 at 13:46
  • Yes please, it will help me to understand the problem. – Alexander Zimin Feb 20 '15 at 13:47
  • Wow, you are so fast!! I still get the error "RegisterViewController does not conform to protocol UIPickerDataSource" when I add the UIPickerViewDataSource – user1555112 Feb 20 '15 at 13:59
  • As I mention at the end, don't forget to implement this protocol in the class declaration. Please compare you first line and my last. – Alexander Zimin Feb 20 '15 at 14:05
  • I updated my code! When I removed the ! from the first 2 functions the error is not shown anymore. I can now run the app, but the picker view is still empty :-( – user1555112 Feb 20 '15 at 14:13
  • Oh, I find that you just add empty picker view gradeTextField.inputView = UIPickerView(), one moment, I will try to fix. – Alexander Zimin Feb 20 '15 at 14:18
  • THANKS SO MUCH!! It works now! I will update my code in my first post! Thanks so much, you helped me a lot!! – user1555112 Feb 20 '15 at 14:30
  • What also help me is that when You want to remove UITextView coursor. Just use `gradeTextField.tintColor = UIColor.clearColor()`. In my opinion it's far better/quicker solution than creating custom class that combine UILabel and UITextView. – Błażej Aug 19 '15 at 12:59
12

First set delegate

UIPickerViewDataSource,UIPickerViewDelegate

set IBOutlet for UIPickerView

@IBOutlet weak var pickerView: UIPickerView!

Take an array for data

var arrayFruits = [String]()

Write this code on viewDidLoad()

  arrayFruits = ["Apple","Banana","Orange","Grapes","Watermelon"]
  self.pickerView.dataSource = self
  self.pickerView.delegate = self

Write picker view delegate methods:

 //MARK: - Pickerview method
func numberOfComponents(in pickerView: UIPickerView) -> Int {
    return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
    return arrayFruits.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
    return arrayFruits[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
    self.labelFruit.text = arrayFruits[row]
}

enter image description here

100% working and tested

Mr.Javed Multani
  • 12,549
  • 4
  • 53
  • 52
  • nice answer but i find it works with "pickerView.delegate = self". Any reason why you have self.pickerView.*? thx – μολὼν.λαβέ Mar 10 '18 at 04:45
  • I am not getting your point. pickerView.delegate = self is used for set the delegate for pickerview so after that all the delegate methods will call. You can also set to it by using storyboard. – Mr.Javed Multani Mar 10 '18 at 19:11
  • isn't viewDidLoad too early for self to be available? – μολὼν.λαβέ Mar 13 '18 at 21:01
  • To get this to work I added `@IBOutlet weak var labelFruit: UILabel!` after the outlet for the UIPickerView and `labelFruit.text = arrayFruits[0]` after the initialisation of `arrayFruits` – paulo62 Mar 28 '20 at 18:11