UIPIckerViews working together in one UIView - ios

looked at the other similar questions but have not found the answers there. Sorted out some of the issues I had but still have the issue where the data from the first pickerView determines what selections there are in the second pickerView (this part of app is a units convertor).
I understand what is going wrong but cannot see how to fix the problem. Through print statements I can see that when I change the first pickerView, the value which holds the number of rows in the second pickerView changes. But the second pickerView titles do not change so that when I go to an item that is higher than index number in the new array, the app crashes. This is confirmed by the error Index out of range.
I have included code and a snapshot of what the pickerViews look like (no formatting or making it pretty - like to get functionality first).
Thanks for any help.
import UIKit
import Foundation
class ViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {
var conversionTypes = ["length", "mass", "area", "volume", "rate", "temp", "pressure"]
var conversionItems = [["metres", "feet", "yard", "inch", "cm"], ["kg", "lbs", "tonne", "ounces"], ["ft2", "m2", "in2"], ["US Gall", "UK Gall", "Bbls", "ft3", "m3"], ["bbl/min", "scf/min", "scf/hr"], ["degC", "degF", "Kelvin"], ["bar", "psi"]]
var littlePickerType = 0
var wheelOne = 0
var wheelTwo = 0
#IBOutlet weak var numberToConvert: UITextField!
#IBOutlet weak var answerLabel: UILabel!
#IBOutlet weak var littlePicker: UIPickerView!
#IBOutlet weak var bigPicker: UIPickerView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
littlePicker.delegate = self
littlePicker.dataSource = self
bigPicker.delegate = self
bigPicker.dataSource = self
let tap = UITapGestureRecognizer(target: self.view, action: #selector(UIView.endEditing))
view.addGestureRecognizer(tap)
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
if pickerView == littlePicker {
return 1
} else if pickerView == bigPicker {
return 2
}
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if pickerView == littlePicker {
return conversionTypes.count
} else if pickerView == bigPicker {
return conversionItems[littlePickerType].count
}
return 1
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if pickerView == littlePicker {
return conversionTypes[row]
} else if pickerView == bigPicker {
return conversionItems[littlePickerType][row]
}
return ""
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if pickerView == littlePicker {
littlePickerType = row
} else if pickerView == bigPicker {
if component == 0 {
wheelOne = row
} else if component == 1 {
wheelTwo = row
}
}
print ("Conversion Type \(littlePickerType) WheelOne \(wheelOne) WheelTwo \(wheelTwo) count \(conversionItems[littlePickerType].count)")
}
}

You should be calling reloadAllComponents on your big picker whenever littlePickerType changes. This causes all the picker view data source methods (especially titleForRow) to be called again for the big picker.
var littlePickerType = 0 {
didSet {
bigPicker.reloadAllComponents()
}
}

Related

I see only ?-s when using 2 pickerviews in Swift on the same view controller, how to fix this?

I have been trying to figure out this problem for hours, now I have only one small problem remaining:
I use 2 UI pickers on one view controller, and when I try to select one (aka I'm clicking into the textfield) all I see is question marks, but if I click on one of them, the text will appear in the textfield. I just don't see what am I choosing in the picker.
I already tried using normal pickerviews, I used tags for each pickers, but nothing seemed to work. I know how it should work, I watched a ton of tutorials, but something is still missing. Can you please help me? Thank you!
Here is the code:
import UIKit
class SelectionViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
#IBOutlet weak var ageTextField: UITextField!
#IBOutlet weak var weightTextField: UITextField!
#IBOutlet weak var genderTextField: UITextField!
#IBOutlet weak var lifeStyleTextField: UITextField!
let picker1 = UIPickerView()
let picker2 = UIPickerView()
var genders = ["Male", "Female"]
var lifeStyle = ["Sitting", "Normal", "Active"]
override func viewDidLoad() {
super.viewDidLoad()
picker1.dataSource = self
picker1.delegate = self
picker2.dataSource = self
picker2.delegate = self
genderTextField.inputView = picker1
lifeStyleTextField.inputView = picker2
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfComponentsInPickerView(pickerView : UIPickerView!) -> Int{
return 2
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int{
if pickerView == picker1 {
return genders.count
} else {
return lifeStyle.count
}
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
if pickerView == picker1 {
return genders[row]
} else if pickerView == picker2{
return lifeStyle[row]
}
else {
print("No pickerview selected.")
}
return ("Pickeview not selected")
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if pickerView == picker1 {
genderTextField.text = genders[row]
self.view.endEditing(false)
} else if pickerView == picker2{
lifeStyleTextField.text = lifeStyle[row]
self.view.endEditing(false)
}
else {
print("Love love love I want your love")
}
}
}
And here's the thing I see:
You need this signature ( missed _ )
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
and numberOfComponentsInPickerView should return 1

Problem using two UIPickerViews - first works, second doesnt

Im trying to use two pickerviews on two different textboxes (i align them underneath the textboxes and hide them until called). I hide the pickerviews until i'm ready to use and tie both textboxes to their corresponding textbox. The first pickerview works fine, but when i click on the second, my list doesnt appear and it appears still hidden. I've read thru the code multiple times and cannot find the issue and have watched multiple videos and checked apples documentation to no avail. Code posted below for review and help. Thanks in advance!
class fifthViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource, UITextFieldDelegate {
#IBOutlet weak var heightInput: UITextField!
#IBOutlet weak var weightInput: UITextField!
#IBOutlet weak var bmiOutput: UITextField!
#IBOutlet weak var weightPicker: UIPickerView!
#IBOutlet weak var heightPicker: UIPickerView!
var height = ["48", "49", "50"]
var weight = ["90", "91", "92"]
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
var countrows : Int = height.count
if pickerView == weightPicker {
countrows = self.weight.count
}
return countrows
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if pickerView == heightPicker {
let titleRow = height[row]
return titleRow
}
else if pickerView == weightPicker {
let titleRow = weight[row]
return titleRow
}
return ""
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if pickerView == heightPicker {
self.heightInput.text = self.height[row]
self.heightPicker.isHidden = true
}
else if pickerView == weightPicker {
self.weightInput.text = self.weight[row]
self.weightPicker.isHidden = true
}
}
func textFieldDidBeginEditing(_ textField: UITextField) {
if (textField == self.heightInput) {
self.heightPicker.isHidden = false
}
else if (textField == self.weightInput) {
self.weightInput.isHidden = false
}
}```
In textFieldDidBeginEditing(), you need to set self.weightPicker.isHidden to false instead of self.weightInput.isHidden.
func textFieldDidBeginEditing(_ textField: UITextField) {
if (textField == self.heightInput) {
self.heightPicker.isHidden = false
}
else if (textField == self.weightInput) {
self.weightPicker.isHidden = false
}
}
Modify your code to use inputViews
I agree with #PaulW11's comment:
"You would be almost certainly better off if you made the picker views
the inputView of each text field."
Here are the changes you need for a basic setup. Get rid of the pickers in the Storyboard and disconnect the #IBOutlet connections.
Then add these modifications:
// These are no longer outlets since they're created in code
var weightPicker: UIPickerView!
var heightPicker: UIPickerView!
override func viewDidLoad() {
super.viewDidLoad()
// set up the picker views
self.weightPicker = UIPickerView()
self.weightPicker.delegate = self
self.weightPicker.dataSource = self
self.heightPicker = UIPickerView()
self.heightPicker.delegate = self
self.heightPicker.dataSource = self
self.heightInput.inputView = self.heightPicker
self.weightInput.inputView = self.weightPicker
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if pickerView == heightPicker {
self.heightInput.text = self.height[row]
// make picker go away after value has been selected
self.heightInput.resignFirstResponder()
}
else if pickerView == weightPicker {
self.weightInput.text = self.weight[row]
// make picker go away after value has been selected
self.weightInput.resignFirstResponder()
}
}

swift 4 multiple picker view not working. I have three arrays ,but only "strBlood" is showing

swift 4 multiple picker view not working. I have three arrays ,but only "strBlood" is showing the accurate array , other strcountry and strgender are not showing in the picker view and when I click on to the third label code crashes giving me the error of signal sigbart
multiple picker views are not showing the data in the picker view in swift 4, I already read many tutorials but no one solve my problem please check and answer me.
when I implemented 3 picker views the data is showing only of the first picker view i.e , (strblood) but not the other arrays
the code below is working properly but there is one error regarding showing the array in the picker view when clciked on the other labels ("lblblood"."lblcountry")
import UIKit
import Foundation
class RegisterViewController: UIViewController {
var strBlood = ["O+","O-","O","A","B+"]
var strcountry = ["India","Canada","USA"]
var strgender = ["Male","Female"]
var selectedBlood: String?
var selectedCountry: String?
var selectedGender: String?
#IBOutlet weak var txtGender: UITextField!
#IBOutlet weak var txtCountry: UITextField!
#IBOutlet weak var lblBloodGroup: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.chooseCountry()
self.chooseBlood()
self.choosGender()
}
func chooseBlood(){
let bloodPicker = UIPickerView()
bloodPicker.delegate = self
self.lblBloodGroup.inputView = bloodPicker
}
func chooseCountry(){
let countryname = UIPickerView()
countryname.delegate = self
self.txtCountry.inputView = countryname
}
func choosGender() {
let gender1 = UIPickerView()
gender1.delegate = self
self.txtGender.inputView = gender1
}
}
extension RegisterViewController : UIPickerViewDelegate , UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if lblBloodGroup.isEnabled {
print("BLOOD SELECTED")
return strBlood.count
}
else if txtCountry.isEnabled{
print("COUNTRY SELECTED")
return strcountry.count
}else {
print("GENDER SELECTED")
return strgender.count
}
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if lblBloodGroup.isEnabled{
print("BLOOD SELECTED1")
return strBlood[row]
}else if txtCountry.isEnabled{
return strcountry[row]
}else {
return strgender[row]
}
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if lblBloodGroup.isEnabled{
selectedBlood = strBlood[row]
lblBloodGroup.text = selectedBlood
}else if txtCountry.isEnabled{
selectedCountry = strcountry[row]
txtCountry.text = selectedCountry
}else {
selectedGender = strgender[row]
txtGender.text = selectedGender
}
}
}
To achieve this isEnabled will not help. you need to use isEditing and it will be true if user click on any UITextField and your code will be:
import UIKit
import Foundation
class ViewController: UIViewController {
var strBlood = ["O+","O-","O","A","B+"]
var strcountry = ["India","Canada","USA"]
var strgender = ["Male","Female"]
var selectedBlood: String?
var selectedCountry: String?
var selectedGender: String?
#IBOutlet weak var txtGender: UITextField!
#IBOutlet weak var txtCountry: UITextField!
#IBOutlet weak var lblBloodGroup: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.chooseCountry()
self.chooseBlood()
self.choosGender()
}
func chooseBlood(){
let bloodPicker = UIPickerView()
bloodPicker.delegate = self
self.lblBloodGroup.inputView = bloodPicker
}
func chooseCountry(){
let countryname = UIPickerView()
countryname.delegate = self
self.txtCountry.inputView = countryname
}
func choosGender() {
let gender1 = UIPickerView()
gender1.delegate = self
self.txtGender.inputView = gender1
}
}
extension ViewController : UIPickerViewDelegate , UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if lblBloodGroup.isEditing {
print("BLOOD SELECTED")
return strBlood.count
}
else if txtCountry.isEditing{
print("COUNTRY SELECTED")
return strcountry.count
}else {
print("GENDER SELECTED")
return strgender.count
}
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if lblBloodGroup.isEditing{
print("BLOOD SELECTED1")
return strBlood[row]
}else if txtCountry.isEditing{
return strcountry[row]
}else {
return strgender[row]
}
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if lblBloodGroup.isEditing{
selectedBlood = strBlood[row]
lblBloodGroup.text = selectedBlood
}else if txtCountry.isEditing{
selectedCountry = strcountry[row]
txtCountry.text = selectedCountry
}else {
selectedGender = strgender[row]
txtGender.text = selectedGender
}
}
}
And result will be:

Using a picker view based on what has been selected before

I looked on here for an answer, but do not believe this question has been answered. I have multiple text boxes that use a couple different pickers. I would like for the first text box the user selects the material, and then based on the material selected they can only see the type of material that corresponds to it. Below in my code when I am naming my arrays you can see that it says typeBrassData or typeCopperData so if the user selects Brass or Copper above only the options for Brass show, does anyone know how to do this? Below is the code I was using when it was only 2 picker views. Thanks all in advance!
import UIKit
class MetalCalculatorViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {
#IBOutlet weak var materialField: UITextField!
#IBOutlet weak var typeField: UITextField!
#IBOutlet weak var volumeField: UITextField!
#IBOutlet weak var quantityField: UITextField!
var pickerMaterial = UIPickerView()
var pickerType = UIPickerView()
var materialData = ["Brass","Copper","CuNiSn", "Cupro-Nickel","High Performance Alloys", "Leaded Brass","Nickel Silvers","Phosphor Bronze","Tin Brass"]
var typeData = ["C101", "C102","C1092","C110","C122","C14415","C151", "C155","C18070", "C18080", "C19020", "C19025", "C19210","C194", "C195", "C197", "C1972", "C210","C220", "C226", "C230", "C240", "C260","C268","C272", "C350", "C353", "C422", "C425", "C4252", "C510","C511", "C5118", "C519","C521", "C638", "C654", "655", "C688", "C7025", "C70250", "C7026", "C7035", "C706", "C710", "C715", "C7250", "C752", "C757","C764","C770", "XP5", "XP10", "XP55", "XP125", "XP150", "XP175", "MAX251C", "MSP1"]
var typeBrassData = ["C210","C220", "C226", "C230", "C240", "C260","C268","C272"]
var typeHighPerformanceData = ["C14415","C151", "C155","C18070", "C18080", "C19020", "C19025", "C19210","C194", "C195", "C197", "C1972", "C638", "C654", "655", "C688", "C7025", "C70250", "C7026", "C7035", "XP5", "XP10", "XP55", "XP125", "XP150", "XP175", "MAX251C", "MSP1"]
var typeCopperData = ["C101", "C102","C1092","C110","C122"]
var typeCuproNickelData = ["C706", "C710", "C715"]
var typeLeadedBrassData = ["C350", "C353"]
var typeTinBrassData = ["C422", "C425", "C4252"]
var typePhosphorBronzeData = ["C510","C511", "C5118", "C519","C521"]
var typeCunNiSnData = ["C725"]
var typeNickelSilverData = ["C752", "C757","C764","C770"]
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewDidLoad() {
super.viewDidLoad()
pickerMaterial.delegate = self
pickerMaterial.dataSource = self
pickerMaterial.tag = 1
materialField.inputView = pickerMaterial
pickerType.delegate = self
pickerType.dataSource = self
pickerType.tag = 2
typeField.inputView = pickerType
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(MetalCalculatorViewController.dismissKeyboard))
view.addGestureRecognizer(tap)
// Do any additional setup after loading the view.
}
func dismissKeyboard() {
view.endEditing(true)
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if pickerView == pickerMaterial {
return materialData.count
} else if pickerView == pickerType{
return typeData.count
}
return 1
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if pickerView == pickerMaterial {
return materialData[row]
} else if pickerView == pickerType{
return typeData[row]
}
return ""
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if pickerView == pickerMaterial {
materialField.text = materialData[row]
} else if pickerView == pickerType{
typeField.text = typeData[row]
}
}
You can use the dictionary data structure to set the values of the pickerviews
var materialSelected = ""
var typeArr = [String]()
//I have done for only three materials obviously you can extend it...
var dict = ["Brass" : ["C210","C220", "C226", "C230", "C240", "C260","C268","C272"],
"Copper" : ["C101", "C102","C1092","C110","C122"], "CuNiSn" : ["C725"]] as [String: Any]
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if pickerView == pickerMaterial {
return materialData.count
} else if pickerView == pickerType{
typeArr = dict[materialSelected] as! [String]
return typeArr.count
}
return 1
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if pickerView == pickerMaterial {
return materialData[row]
} else if pickerView == pickerType{
typeArr = dict[materialSelected] as! [String]
return typeArr[row]
}
return ""
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if pickerView == pickerMaterial {
materialSelected = materialData[row]
materialField.text = materialSelected
} else if pickerView == pickerType{
typeArr = dict[materialSelected] as! [String]
typeField.text = typeArr[row]
}
}

UIPickerView not registering the correct last row. Swift 2.0

I have a 2 UIPickerView's, one called dayPicker where you have an option between 1-12 and durationPicker where you have the options of days, weeks, months and years.
The code for this is below:
class AddNewViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate, UITextFieldDelegate {
#IBOutlet weak var dayPicker: UIPickerView!
#IBOutlet weak var durationPicker: UIPickerView!
var durationDay: Double?
var durationType: String?
var durationPickerDataSource = ["Day(s)","Week(s)","Month(s)","Year(s)"];
var dayPickerDataSource = ["1","2","3","4","5","6","7","8","9","10","11","12"];
func numberOfComponentsInPickerView(dayPickerView: UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if pickerView == durationPicker {
return durationPickerDataSource.count;
}
else {
return dayPickerDataSource.count;
}
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if pickerView == durationPicker{
durationType = durationPickerDataSource[row]
print("")
print("")
print("\(row): type == \(durationType)")
print("\(row)")
print("")
print("")
return durationType
}
else {
durationDay = Double(dayPickerDataSource[row])
return dayPickerDataSource[row]
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.durationPicker.dataSource = self;
self.durationPicker.delegate = self;
self.dayPicker.dataSource = self;
self.dayPicker.delegate = self;
}
}
when I select the rows in the simulator it prints all of the rows correctly apart from the last one where it prints "Week(s)".
I have the following constraints on the pickers:
dayPicker:
durationPicker:
Copied your code, everything seems to work fine. My only guess is that you are modifying your data sources and therefore the picker delegate returns the wrong information.
I tested your code (without modifications), the pickers display the correct values...
EDIT:
From the comment section it seems the problem lies in not having specified the UIPickerView's didSelectRow method, adding it seemingly solved the OP's problem (can't elaborate on the "why" though...):
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if pickerView == self.dayPicker{
print("picked \(dayPickerDataSource[row])")
}else {
print("picked \(durationPickerDataSource[row])")
}
}

Resources