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()
Related
In my searches, I see this is a common question but I've tried some of the solutions and haven't been able to resolve. A simple example below. When I run the app, and press button, 0 prints as expected. After pressing button2, then button, 1 prints, also as expected. If I move the picker, then press button, I expect 2, but still get 1.
I gather this is because ViewController().sample() is creating a copy, and not actually changing the variable in the "original" ViewController(). I'm stumped at what needs to be reconfigured to get the desired behavior.
import UIKit
class pickerHelper: 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) {
ViewController().sample()
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
p1.delegate = helper1
p1.dataSource = helper1
}
#IBOutlet weak var p1: UIPickerView!
let helper1 = pickerHelper(data:["a", "b", "c"])
var test = Int()
#IBAction func button(_ sender: Any) {
print(test)
}
#IBAction func button2(_ sender: Any) {
test = 1
}
func sample() {
test = 2
}
}
This is a situation where you would either use a delegation pattern or a callback closure; this latter is more "Swifty".
With delegation:
protocol PickerHelperDelegate {
func pickerHelper(_ helper: selectedRow row:)
}
Then in your PickerHelper you add a delegate property and invoke the delegate when required:
class PickerHelper: NSObject, UIPickerViewDataSource, UIPickerViewDelegate {
let data: [String]
var delegate: PickerHelperDelegate?
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) {
self.delegate?.pickerHelper(self, selectedRow: row)
}
}
In your view controller you need to assign the delegate and implement the protocol function:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
p1.delegate = helper1
p1.dataSource = helper1
helper1.delegate = self // Assign this object as the picker helper's delegate
}
#IBOutlet weak var p1: UIPickerView!
let helper1 = pickerHelper(data:["a", "b", "c"])
var test = Int()
}
extension ViewController: PickerHelperDelegate {
func pickerHelper(_ helper: PickerHelper, selectedRow row:Int) {
self.sample()
print("Selected row \(row)")
}
}
You can do a similar thing with a closure:
class PickerHelper: NSObject, UIPickerViewDataSource, UIPickerViewDelegate {
let data: [String]
var changeHandler: ((Int)->Void)?
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) {
self.changeHandler?(row)
}
}
In your view controller you need to assign a closure to the changeHandler property:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
p1.delegate = helper1
p1.dataSource = helper1
helper.changeHandler = { row in
self.sample()
print("Selected row \(row)")
}
}
You could also change your protocol or closure to pass back the data element rather than just the row number and use generics for your PickerHelper so that it wasn't just limited to strings.
Note that classes start with a capital by convention, so you should say PickerHelper, not pickerHelper.
You can use the static property to keep its state in class.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
p1.delegate = helper1
p1.dataSource = helper1
}
#IBOutlet weak var p1: UIPickerView!
let helper1 = pickerHelper(data:["a", "b", "c"])
static var test = Int()
#IBAction func button(_ sender: Any) {
print(test)
}
#IBAction func button2(_ sender: Any) {
ViewController.test = 1
}
static func sample() {
test = 2
}
}
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.
We are trying to delete data from a UIPicker view upon selection, however, it is deleting only the first string "Select You're Time" upon election of any string in UIPicker. After going back, "Select You're Time" is also reloaded. The reload function is required otherwise our code doesn't delete anything.
import Foundation
import UIKit
import Firebase
import FirebaseDatabase
class timeSinglesViewController:UIViewController,UIPickerViewDataSource,UIPickerViewDelegate {
#IBOutlet weak var myPicker: UIPickerView!
#IBOutlet weak var myLabel: UITextField!
var pickerData = ["Select You're Time","6:30", "7:30", "8:30", "9:30", "10:30", "11:30", "12:30", "1:30", "2:30", "3:30", "4:30", "5:30", "6:30", "7: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 row = Int()
let ref = FIRDatabase.database().referenceFromURL("deleted url")
ref.child("Time").setValue(myLabel.text)
pickerData.removeAtIndex(row)
myPicker.reloadAllComponents()
}
}
Any thoughts are appreciated. Thank you!
I have a UIPickerView I need to save the users selection to Parse. I've tried a million different things and it just leaves my column blank. Any ideas what I am doing wrong? The information is just not saving to Parse, I know the UIPicker works though because the label updates. I have searched google and stack and can't see anything wrong with my code?
class ViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {
#IBOutlet weak var pickerView: UIPickerView!
#IBOutlet weak var pickedLabel: UILabel!
var pickerDataSource = ["Car", "Bus", "Train", "Plane", "Boat"]
override func viewDidLoad() {
super.viewDidLoad()
self.pickerView.dataSource = self;
self.pickerView.delegate = self;
}
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return self.pickerDataSource.count;
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
return self.pickerDataSource[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int)
{
PFUser.currentUser()?["PickerColumn"] = self.pickerDataSource[row];
self.pickedLabel.text = "Your choice: \(self.pickerDataSource[row])"
PFUser.currentUser()?.saveInBackground()
}
I've also tried this:
var pickerSave: String?
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int)
{
pickerSave = pickerDataSource[row]
self.pickedLabel.text = "Your choice: \(self.pickerDataSource[row])"
}
#IBAction func next(sender: AnyObject) {
PFUser.currentUser()?["pickerColumn"] = pickerSave
self.performSegueWithIdentifier("next", sender:self)
PFUser.currentUser()?.saveInBackground()
}
If your label is updating then your picker is fine. You issue is the PFUser class. Is PFUser.currentUser() returning a dictionary as you expect? after you set "pickerColumn" can you print out its value on the console?
Does the following work:
PFUser.currentUser()?["pickerColumn"] = "1"
PFUser.currentUser()?.saveInBackground()
Those specific lines are what is breaking.
So It looks like you are trying to save the value from the picker into the parse _User class. I wouldn't recommend saving it there anyway. Im assuming you are using a mongoDB and this is a schema issue. So what I would do is make a new class for storing this data.
I would declare it like so.
var pickerData = PFObject(className:"PickerData")
#IBAction func next(sender: AnyObject) {
pickerData["user"] = PFUser.current()?
pickerData["data"] = self.pickedLabel.text
pickerData.saveInBackground()
}
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