Determining which UIPicker's toolbar button sent a message - ios

I am trying to use multiple UIPickers on a page. I think I've configured almost everything. When I use a UIPicker I add in a a tool bar with a "Cancel" and a "Done" Button.
The toolbars are built using the same function, so it calls the same action when a user taps "Done". A pseudo example flow below.
let pickerData: [String] = ["1", "2", "3", "4"]
var picker = UIPickerView()
let pickerData2: [String] = ["A", "B", "C", "D"]
var picker2 = UIPickerView()
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var textField2: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// set up pickers
setPickerView()
}
// set the pickerview
func setPickerView(){
picker.dataSource = self
picker.delegate = self
textField.inputView = picker
textField.inputAccessoryView = initToolBar()
// second picker
picker2.dataSource = self
picker2.delegate = self
textField2.inputView = picker
textField2.inputAccessoryView = initToolBar()
}
// build the toolbar for uipicker, so a user can select a value
func initToolBar() -> UIToolbar {
let toolBar = UIToolbar()
toolBar.barStyle = UIBarStyle.default
toolBar.isTranslucent = true
toolBar.tintColor = UIColor(red:14.0/255, green:122.0/255, blue:254.0/255, alpha: 1)
toolBar.sizeToFit()
// TODO need to update actions for all buttons
let cancelButton = UIBarButtonItem(title: "Cancel", style: UIBarButtonItemStyle.plain, target: self, action: nil)
let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.plain, target: self, action: #selector(pickerDoneAction))
let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil)
toolBar.setItems([cancelButton, spaceButton, doneButton], animated: false)
toolBar.isUserInteractionEnabled = true
return toolBar
}
func pickerDoneAction(sender: UIBarButtonItem){
/*Here I'm just updating the same textfield when the done button is
is pressed, I'm not sure how I can get the instance of the picker into this method*/
let indexPath = picker.selectedRow(inComponent: 0)
textfield.text = data[indexPath]
}
// delegates
func numberOfComponents(in pickerView: UIPickerView) -> Int { return 1 }
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if pickerView == picker {
return pickerData.count
} else return pickerData2.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if pickerView == picker {
return pickerData[row]
} else return pickerData2[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if pickerView == picker {
textField.text = pickerData[row]
} else textField2.text = pickerData2[row]
}
Is there a way to determine which UIPicker was being used when the done button was clicked, so I could use that instance in my pickerDoneAction method and then change the corresponding textfield's value? I tried checking if the textfield was firstResponder and even tried to check the UIPicker itself, but it didn't seem possible. Do I need to change my approach and if so how?
Update
I've added in my existing UIPicikerView delegate methods.
Just to clarify, I want the value of the UIPicker to be assigned to the correct textfield when a user presses "Done", therefore if a user wants to select the very first row from a UIPicker, it gets inserted.

You can implement the following UIPickerViewDelegate's method:
optional func pickerView(_ pickerView: UIPickerView,
didSelectRow row: Int,
inComponent component: Int)
where the method is Called by the picker view when the user selects a row in a component and use
pickerView
(An object representing the picker view requesting the data) to know which pickerview has been used.

You can always pass down data using UIView's tag property:
func setPickerView() {
picker.tag = 1
textField.inputView = picker
textField.inputAccessoryView = initToolBar(pickerTag: picker.tag)
picker2.tag = 2
textField.inputView = picker2
textField.inputAccessoryView = initToolBar(pickerTag: picker2.tag)
}
func initToolBar(pickerTag: Int) {
let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.plain, target: self, action: #selector(pickerDoneAction))
doneButton.tag = pickerTag
}
func pickerDoneAction(sender: UIBarButtonItem){
let pickerView: UIPickerView
switch sender.tag {
case picker.tag: pickerView = picker
case picker2.tag: pickerView = picker2
default: return
}
}

You can try changing data dynamically with different conditions that you set as data source to pickerview

Maybe you don't need to determine which picker it is, but which text field.
In pickerDoneAction(sender:), use let textField = sender.superview.superview, where sender is the button, parent is the toolbar and the toolbar's parent is the text field. Then you fill it with pickerData[0].
Another way to achieve the same goal is to use UITextFieldDelegate's method textFieldDidEndEditing(_:). When the text field ends editing, insert the default value there.

let pickerData: [String] = ["1", "2", "3", "4"]
var picker = UIPickerView()
let pickerData2: [String] = ["A", "B", "C", "D"]
var picker2 = UIPickerView() // <<<<<<<<<<<<<
var userPicker:UIPickerView?
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var textField2: UITextField!
.....
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
userPicker = pickerView // <<<<<<<<<<<<<
if pickerView == picker {
return pickerData[row]
} else return pickerData2[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
userPicker = pickerView // <<<<<<<<<<<<<
if pickerView == picker {
textField.text = pickerData[row]
} else textField2.text = pickerData2[row]
}

You can simply check which textfield is in editing state. Do this in your done clicked function. You don't have to add any tags to the pickerView or to the textField.
func pickerDoneAction(sender: UIBarButtonItem) {
if textField1.isEditing == true {
Print("pickerview 1 is selected")
} else {
Print("pickerview 2 is selected")
}
}
Hope this helps! :)

Related

Problem populating textfield from UIPickerView in an AlertController

Noob with a problem. I'm trying to use an alert controller that will take two inputs: one from a textfield, which will be manually entered text, the other is a selection from a UIPicker. The text portion works fine; the problem I'm having is with the Picker.
The PickerView displays as I want; the problem I am having is registering the selection. As a noob, I full anticipate that is something idiotically stupid that I'm doing (which is why you see all the print statements); however, after many attempts and searches, I can't figure out what that stupid mistake is that I'm missing. I've tried borrowing from other solutions, which is why the code is getting a bit messy, and why I'm now turning here for help.
Thanks for any assistance you can provide
import UIKit
import PMAlertController
class TableViewController: UITableViewController {
var dummyArray = ["A", "B", "C", "D", "E"]
var dummyStores = ["1", "2", "3", "4"]
var inputString: String = ""
var pickerView = UIPickerView()
var selectedStore: String?
var storeTextField: String?
var textLabel = UITextField()
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dummyArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell", for: indexPath)
cell.textLabel?.text = dummyArray[indexPath.row]
cell.textLabel?.textAlignment = .center
return cell
}
#IBAction func addButtonPressed(_ sender: UIBarButtonItem) {
print ("Add Button Pressed")
var itemTextField = UITextField()
var storeTextField = UITextField()
let screenSize = UIScreen.main.bounds
let alertVC = PMAlertController(title: "A Title", description: "My Description", image: UIImage(named: "img.png"), style: .alert)
let pickerFrame = UIPickerView(frame: CGRect(x:5, y: 20, width: screenSize.width - 20, height: 140))
pickerFrame.tag = 555
pickerFrame.delegate = self
pickerView.delegate = self
pickerView.dataSource = self
alertVC.addTextField { (textField2) in
textField2?.placeholder = "enter item name here"
itemTextField = textField2!
}
alertVC.addTextField { (textField) in
textField?.placeholder = "select store here"
textField?.inputView = pickerView
pickerView.delegate = self
pickerView.dataSource = self
let toolbar = UIToolbar()
toolbar.barStyle = UIBarStyle.default
toolbar.isTranslucent = true
textField?.inputAccessoryView = toolbar
textField?.inputView = pickerView
storeTextField = textField!
}
alertVC.addAction(PMAlertAction(title: "Cancel", style: .cancel, action: { () -> Void in
print("Capture action Cancel")
}))
alertVC.addAction(PMAlertAction(title: "OK", style: .default, action: { () in
print("Capture action OK")
print(itemTextField.text)
print(storeTextField.text)
self.dummyArray.append(itemTextField.text!)
self.tableView.reloadData()
}))
self.present(alertVC, animated: true, completion: nil)
}
}
extension TableViewController: UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return dummyStores.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return dummyStores[row]
}
}
extension TableViewController: UIPickerViewDelegate {
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
selectedStore = dummyStores[row]
textLabel.text = selectedStore
}
}
Seems you created a text field textLabel (confusing name by the way, because of UILabel) which you use in the picker view delegate selection method, but when you created the text field that triggers the picker view, you aren't connecting the text field to the class' corresponding property. Instead you have created a local variable storeTextField and set that to the text field.
This won't do anything because you lose the reference to that text field as soon as the context clears. Essentially you could replace:
storeTextField = textField!
with:
self.textLabel = textField!
And you should presumably see what you are aiming to accomplish.
You also have a property called storeTextField but that's a String? for some reason.
Just a tip: you (and others on SO) will find it much easier to debug your code if you use meaningful variable names that are consistent with the types they represent. You also have a lot of redundant / unnecessary code in your code sample, such as when you create a UIPickerView instance called pickerFrame that you never use, when you initialize storeTextField and itemTextField but then immediately replace those variables with new values, or when you set the pickerView dataSource and delegate or set the inputView multiple times.

Swift - Programmatically create UIPickerView

I created PickerView, but this does not open the PickerView I created. Where do I make a mistake? Why PickerView doesn't open. I added picker view in the text field but it doesn't work. I added it to viewDidLoad, but when I click a text field, picker view does not open.
class EnergyChart: UIViewController , UIPickerViewDelegate, UIPickerViewDataSource {
var picker = UIPickerView()
var gradePickerValues1 : [String] = ["...", ...]
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return gradePickerValues1.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return gradePickerValues1[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int){
getDevice.text = gradePickerValues1[row]
switch row {
case 0: break
default: break
}
self.view.endEditing(true)
}
#objc func cancelTapped() {
self.view.endEditing(true)
}
let getDevice: UITextField = {
let textFieldframe = CGRect()
let textField2 = SkyFloatingLabelTextFieldWithIcon(frame: textFieldframe, iconType: .image)
...
return textField2
}()
#objc func GetDevice() {
self.getDevice.text = self.gradePickerValues1[0]
}
override func viewDidLoad() {
super.viewDidLoad()
picker.backgroundColor = .white
picker.showsSelectionIndicator = true
picker.delegate = self
picker.dataSource = self
getDevice.inputView = picker
GetDevice()
let toolBar = UIToolbar()
toolBar.barStyle = UIBarStyle.default
toolBar.isTranslucent = true
toolBar.tintColor = UIColor(..)
toolBar.sizeToFit()
let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil)
let cancelButton = UIBarButtonItem(title: "İptal", style: UIBarButtonItem.Style.plain, target: self, action: #selector(cancelTapped))
toolBar.setItems([cancelButton, spaceButton], animated: false)
toolBar.isUserInteractionEnabled = true
getDevice.inputAccessoryView = toolBar
view.addSubview(getDevice)
getDevice.snp.makeConstraints { (make) in
make.centerX.equalTo(view).offset(50)
make.top.equalTo(view).offset(510)
make.height.equalTo(50)
make.width.equalTo(200)
}

How to use UIPickerview and UIDatePicker on the same Viewcontroller?

I have three TextFields in a Viewcontroller. After clicking the first I want to use UIPickerview to select something. The second TF should open the keyboard and the third one should open UIDatepicker.
import UIKit
class NewCompetitionViewController: UIViewController, UITextViewDelegate,UIPickerViewDataSource, UIPickerViewDelegate{
#IBOutlet weak var programTextField: UITextField!
let program = ["Big Target - 60 Shots",
"Big Target - 30 Shots",
"Small Target - 40 Shots"]
var selectedProgram: String?
let datePicker = UIDatePicker()
#IBOutlet weak var locationTextField: UITextField!
#IBOutlet weak var dateTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
createProgramPicker()
createDatePicker()
}
#IBAction func saveButton(_ sender: UIButton) {
dismiss(animated: true)
}
#IBAction func cancelButton(_ sender: UIButton) {
dismiss(animated: true)
}
// PROGRAM PICKER
func createProgramPicker(){
let programPicker = UIPickerView()
let toolBarPP = UIToolbar()
programPicker.delegate = self
// Customizations
programPicker.backgroundColor = .white
toolBarPP.sizeToFit()
let spaceButtonPP = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let doneButtonPP = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(dismissKeyboard))
toolBarPP.setItems([spaceButtonPP, doneButtonPP], animated: false)
//toolBarPP.isUserInteractionEnabled = true
programTextField.inputAccessoryView = toolBarPP
programTextField.inputView = programPicker
}
// DISMISS KEYBOARD
#objc func dismissKeyboard(){
self.view.endEditing(true)
}
// DATE PICKER
func createDatePicker(){
datePicker.datePickerMode = .date
datePicker.backgroundColor = .white
let toolbarDP = UIToolbar()
toolbarDP.sizeToFit()
// flexible Space Button and Done button
let spaceButtonDP = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let doneButtonDP = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(doneClicked))
toolbarDP.setItems([spaceButtonDP, doneButtonDP], animated: false)
//toolbarDP.isUserInteractionEnabled = true
dateTextField.inputAccessoryView = toolbarDP
dateTextField.inputView = datePicker
}
#objc func doneClicked(){
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/YYYY"
dateFormatter.dateStyle = .medium
dateFormatter.timeStyle = .none
dateTextField.text = dateFormatter.string(from: datePicker.date)
self.view.endEditing(true)
}
// Returns true after "Done" clicked
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
// Hide the keyboard
textField.resignFirstResponder()
return true
}
private func textFieldDidEndEditing(_ textField: UITextField){
locationTextField.text = textField.text
}
}
extension NewCompetitionViewController: UIPickerViewDelegate, UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return program.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return program[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
selectedProgram = program[row]
programTextField.text = selectedProgram
}
}
I can start the simulation and the UIPickerview works. Also the input on the keyboard at the second textfield. But if I open the datePicker and I click the done-button the display slides up from the bottom to the top with a transparent blue look.
Here are some screenshots:
Main screen
Program selection
Date selection
Bluescreen after clicking done

UIPickerView won't select the first row unless I select a different row first?

It seems that I cannot select the very first row in the UIPickerView when I first load it. I want the textfield to update immediately when I load the picker view. It only updates when I select something else, and then go back to the first row.
var platformData = ["XBOX", "PS4", "PC"]
var picker = UIPickerView()
#IBOutlet var platformSelected: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
picker.delegate = self
picker.dataSource = self
platformSelected.inputView = picker
let toolbar = UIToolbar()
toolbar.sizeToFit()
let doneButton = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(self.doneAction))
toolbar.items = [doneButton]
platformSelected.inputAccessoryView = toolbar
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return platformData.count
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
platformSelected.text = platformData[row]
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return platformData[row]
}
Improve your code.
platformSelected.delegate = self
......
// MARK: - UITextFieldDelegate
extension YourUIViewController: UITextFieldDelegate {
func textFieldDidBeginEditing(_ textField: UITextField) {
// TODO: - Specify index what you need. After editing it changed and you should get selected index using its position in platformData Array
let index = 0
picker.selectRow(index, inComponent: 0, animated: true)
}
}

First iOS app in Swift - Question marks in UIPickerView

I'm working on my first Swift ios application.
Can't get data from pickerData into my picker, but I only get question marks instead of real values. I guess it's something to do with delegate, but not sure what wrong.
import UIKit
import CoreData
class NewWorkoutController: UIViewController,UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var workoutDistance: UITextField!
let pickerData = ["11","12","13","14","15"]
// Data Sources
func numberOfComponentsInPickerView(distancePickerView: UIPickerView) -> Int {
return 1
}
func pickerView(distancePickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return pickerData.count
}
// Delegates
func pickerViewReturnRow(distancePickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
return pickerData[row]
}
func pickerViewText(distancePickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
workoutDistance.text = pickerData[row]
}
func doneDistancePicker() {
workoutDistance.resignFirstResponder()
}
func cancelDistancePicker() {
workoutDistance.resignFirstResponder()
}
#IBAction func textFieldDistanceEditing2(sender: UITextField) {
// Create picker view
var distancePickerView: UIPickerView
distancePickerView = UIPickerView(frame: CGRectMake(0, 200, view.frame.width, 300))
distancePickerView.backgroundColor = .whiteColor()
distancePickerView.showsSelectionIndicator = true
distancePickerView.delegate = self
distancePickerView.dataSource = self
// Create toolbar
var 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()
// Create buttons
var doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.Plain, target: self, action: "doneDistancePicker")
var spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FlexibleSpace, target: nil, action: nil)
var cancelButton = UIBarButtonItem(title: "Cancel", style: UIBarButtonItemStyle.Plain, target: self, action: "cancelDistancePicker")
// Assign buttons to toolbar
toolBar.setItems([cancelButton, spaceButton, doneButton], animated: false)
toolBar.userInteractionEnabled = true
// Add pickerview and toolbar to textfield
workoutDistance.inputView = distancePickerView
workoutDistance.inputAccessoryView = toolBar
sender.inputView = distancePickerView
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
The signature for this function is wrong:
func pickerViewReturnRow(distancePickerView: UIPickerView,
titleForRow row: Int,
forComponent component: Int) -> String!
It should be
func pickerView(pickerView: UIPickerView,
titleForRow row: Int,
forComponent component: Int) -> String!
As a result that method (which provides titles for the rows in your picker) isn't getting called.
In all pickerView delegate method's signature, you replaced pickerView with your pickerView outlet that is distancePickerView which is not required. You should modify code inside delegate method's body because delegate methods are automatically called by swift compiler if we confirm your pickerView delegate and datasource to your ViewController class by writing code as:
pickerViewOutlet.delegate = self
pickerViewOutlet.dataSource = self
So just modify all pickerView delegate methods with its default signature then it will get called by compiler and data will display in it.

Resources