UIPickerView best practice? - ios

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...

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]
}
}

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]
}
100% working and tested

Related

Get selected value from picker view (picker wheel)

I'm currently using XCode version 11.3.1 (11C505) and Swift 5 and I can't seem to find a good answer as to how to get the selected value from a picker wheel (view). Here's the relevant code from my ViewController.swift
class ViewController: UIViewController {
#IBOutlet weak var numberPicker: UIPickerView!
#IBOutlet weak var test: UILabel!
private let possibleNums: [Int] = Array(1...16) // Create an array of Ints from 1 to 16
override func viewDidLoad() {
super.viewDidLoad()
numberPicker.dataSource = self
numberPicker.delegate = self
}
}
extension ViewController: UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return possibleNums.count
}
// Attempt at getting the selected value, didn't work
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int {
print(possibleNums[numberPicker.selectedRow(inComponent: 0)])
test.text = "\(possibleNums[numberPicker.selectedRow(inComponent: 0)])"
}
}
extension ViewController: UIPickerViewDelegate {
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return String(possibleNums[row])
}
}
Edit: Solution was also in comments so I figured I'd put it here in case anyone else finds this:
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int should be func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int (missing _)
and test.text = "\(possibleNums[numberPicker.selectedRow(inComponent: 0)])" should just be test.text = "\test.text = "\(possibleNums[row])"
Change test.text = "\(possibleNums[numberPicker.selectedRow(inComponent: 0)])" to this: test.text = "\(possibleNums[row])"
You only have one component, so you don't need to check for it, just return the row number as the array's index.
The easiest way to do that is to change your didSelectRow function from
test.text = "\(possibleNums[numberPicker.selectedRow(inComponent: 0)])"
to
test.text = "\(possibleNums[rows])"

Creating Second Delegate

I'm trying to create a second UIPickerView delegate so I can control them individually instead of using if blocks. Below is an example I put together, I'm unsure of how to connect the second UIPickerView outlet to the second UIPickerView delegate (assuming that is all I am missing).
import UIKit
protocol secondDelegate {
func secondPickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int
func secondNumberOfComponents(in pickerView: UIPickerView) -> Int
}
protocol secondDelegateDataSource {
func secondPickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String?
}
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource, secondDelegate, secondDelegateDataSource {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
p1.delegate = self
p1.dataSource = self
p2.delegate = self
p2.dataSource = self
}
#IBOutlet weak var p1: UIPickerView!
#IBOutlet weak var p2: UIPickerView!
var delegate2: secondDelegate?
var dataSource2: secondDelegateDataSource?
let picker1 = ["1","3","5"]
let picker2 = ["2","4","6"]
// first picker
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return picker1.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return picker1[row]
}
// second picker
func secondNumberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func secondPickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return picker2.count
}
func secondPickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return picker2[row]
}
}
So here's the problem: you are saying the words "second delegate", but in fact you still have just one delegate — the view controller.
That's your mistake. Make your deeds match your words. Get all the delegate code out of the view controller. Instead, give your view controller two helper objects — the two picker delegates. Route the dataSource and delegate properties of the two pickers correctly, one to each delegate. Now the messages are coming and going to completely separate places, and you will not have any single bottleneck with if clauses everywhere as you do now.
Here's a simplified example where the two helpers are instance of one class — naturally, nothing requires that you do it that way:
class Helper: NSObject, UIPickerViewDataSource, UIPickerViewDelegate {
let data: [String]
init(data: [String]) {
self.data = data
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
data.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
data[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
print(data[row])
}
}
class ViewController: UIViewController {
#IBOutlet weak var p1: UIPickerView!
#IBOutlet weak var p2: UIPickerView!
let helper1 = Helper(data:["Manny", "Moe", "Jack"])
let helper2 = Helper(data:["Harpo", "Chico", "Groucho"])
override func viewDidLoad() {
super.viewDidLoad()
p1.delegate = helper1
p1.dataSource = helper1
p2.delegate = helper2
p2.dataSource = helper2
}
}
If the two picker views do very different kinds of thing, then you would want two separate Helper classes, each of which acts as a picker view delegate in its own way. Again, the goal here is to avoid having to examine the pickerView in the datasource and delegate methods and decide with an if clause what to do; each Helper type would do just one thing.

How to get data selected from UIPickerView and display it on a different View Controller?

I would like to show a user different screens depending on what they choose in a UIPickerView. What would be the best way to implement this? Thanks!
import UIKit
class SelectKitViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var kitPickerView: UIPickerView!
let kits = ["Kit 1", "Kit 2"]
var chosenKit: String?
override func viewDidLoad() {
super.viewDidLoad()
kitPickerView.delegate = self
kitPickerView.dataSource = self
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return kits[row]
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return kits.count
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
let value = kits[row]
chosenKit = value
}
#IBAction func continuePressed(_ sender: Any) {
performSegue(withIdentifier: "SelectReactionVolume", sender: nil)
}
}
I would like to perform a segue to another View Controller when continue is pressed. Once the user chooses a kit they will have to enter additional info about the kit selected.

Deleting string from UIPicker upon selection

I am creating a booking app. I am using UIPickers to display open times. I have the pickers fully functional but am not sure how to delete booked times after a user has selected one from the choices provided. I am relatively new to Xcode, so any information is greatly appreciated!!
import Foundation
import UIKit
import Firebase
import FirebaseDatabase
class sTimeSelectionViewController:UIViewController,UIPickerViewDataSource,UIPickerViewDelegate {
#IBOutlet weak var myPicker: UIPickerView!
#IBOutlet weak var myLabel: UILabel!
let pickerData = ["9:30","10:30","11:30","12:30","1:30","2:30"]
override func viewDidLoad() {
super.viewDidLoad()
myPicker.dataSource = self
myPicker.delegate = self
myPicker.tag = 1
}
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) {
myLabel.text = pickerData[row]
}
#IBAction func postData(sender: AnyObject) {
let ref = FIRDatabase.database().referenceFromURL("deleted URL")
ref.child("Time").setValue(myLabel.text)
}
}
Thanks!
First change your pickerData declaration from constant to variable
var pickerData = ["9:30","10:30","11:30","12:30","1:30","2:30"].
Next you would need to remove the array element in your method didSelectRow after your myLabel.text code
pickerData.removeAtIndex(row)
and reload your picker
myPicker.reloadAllComponents()

Swift-XCode - How to let Text Field have set values the user can choose from?

I understand how the normal text field works where the user can input their own text, but is there a way where the user clicks the text field and it comes up with some options, such as "Hello", "Bye", and "Goodnight"?
It's sort of like a placeholder with more options, and the placeholder really isn't in effect in this because when the user clicks the text field, the options pop up for text to select, and the user can select the text and that text will be used in the text field.
Thanks!
You want to use UIPickerView and the inputView property of UITextField
Implement the methods like:
class ViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate
{
#IBOutlet weak var textField: UITextField!
var dataObject : [String] = ["Hello","Bye","Good Night"];
override func viewDidLoad()
{
super.viewDidLoad()
let picker = UIPickerView()
picker.delegate = self
picker.dataSource = self
self.textField.inputView = picker
}
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int
{
return 1;
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int
{
return self.dataObject.count;
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String!
{
return self.dataObject[row];
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int)
{
self.textField.text = self.dataObject[row];
self.textField.endEditing(true)
}
}

Resources