I'm a newbie with IOS development so please keep this in mind with my question. I'm working on an app that will show an initial array. Based on the selection of the array, the second pickerview (shown in the next view controller) will use a dictionary and the selected option from the first pickerview to populate the second pickerview with a list of subtopics. The console is showing that all of the data is passing correctly through the process, however the data is not populating within the second pickerview. Can someone take a look at my code and tell me where I'm going wrong?
import UIKit
import Foundation
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var pickerView: UIPickerView!
#IBAction func SubjectSelected(_ sender: UIButton, forEvent event: UIEvent) {
SubjectHandler = subSubjects[SelectedSubject]!
print(SubjectHandler)
print(Subjects)
}
//Create instances of the selected subject & the subject handler (in case SelectedSubject is not empty)
var SelectedSubject: String = ""
var SubjectHandler: Array<String> = []
//Define primary values for both subjects and subSubjects//
let Subjects: Array = ["Macroeconomics", "Microeconomics", "Financial Economics", "Game Theory", "Econometrics", "Law Economics", "Public Sector Economics", "International Economics", "General Statistics"]
let subSubjects = [
"Macroeconomics":
["GDP", "Accounting Methods", "Labor Market", "DMP Job Search Model", "Keynesian Economics", "Sticky-Price Model", "Elasitcity", "Supply and Demand"],
"Microeconomics":
["Elasticity", "Consumer Theory", "Preference Curves", "Competitive Models", "Scarcity"],
"Financial Economics":
["Interest", "Dividends Returns", "Return & Expected Return"],
"Game Theory":
["Nash Equilibrium", "Dominant Strategies", "Iterated Games", "Backwards Induction", "Extensive Form Games"],
"Econometrics":
["Capital Asset Pricing Model", "Regressional Analysis", "Modeling Rules", "Central Limit Theorem", "Probablitiy & Distribution","Heteroscedasticity", "Weighted Least Squares", "Sampling Distributions"],
"Law Economics":
["Externalities", "Damages Calculations", "Property Rights Ownership", "Claims", "Ownership Principles"],
"Public Sector Economics": ["Valuations", "Tax Income Calculations", "Budget Analysis"], "International Economics": ["Taxing Methods", "Importing and Exporting", "Currency Exchange Rates", "Tarrifs", "GDP", "Product Purchase Parity"],
"General Statistics":
["Z-Scores", "Summary Statistics", "One-Tailed Hypothesis Testing", "Two-Tailed Hypothesis Testing", "Confidence Intervals", "Upper and Lower Bounds"]
]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
pickerView.delegate = self
pickerView.dataSource = self
if pickerView.tag == 2 {
pickerView.reloadAllComponents()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if pickerView.tag == 1 {
return Subjects.count
}
else {
return SubjectHandler.count
}
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if pickerView.tag == 1 {
return Subjects[row]
}
else {
return SubjectHandler[row]
}
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
SelectedSubject = Subjects[row]
}
}
As you have said, you use a separate viewController object at the second screen. When it gets loaded it will have an empty SubjectHandler array.
You sould pass the array between viewControllers. See this tutorial
Related
The first text field is a Int Value where you insert your current sold. A second one textfield where I get some Int value and add to a tableView with a button. The value you add into the second textfield soustract to the main value ( in that case 2500 ). That's working great.
My problem is to attribuate the PickerValue to the TableRow Value. it's working but not like I want.
I want to save the pickerValue when i click onto the button. But when i enter a new value it's changing all row of the pickerValue. In that case, you can see two row with "telephone". Normaly it's would be "telephone" and something else ( "maison" or "house").
Do you see a reason why it's doing like this ? I wish you can understand what i mean.
problem with picker value save into table view (gif)
#IBAction func addBtn(_ sender: Any) {
view.endEditing(true)
salaire = Int(salaireLabel.text!) ?? 0
valeur = Int(ressourceTextField.text!) ?? 0
if String(valeur) != "" {
addBtnActivated = true
restValueLabel.textColor = UIColor.blue
arrayRessource.append(valeur)
// modifie automatiquement le salaire
restValueLabel.text = String(soustraction)
tableRessourceOT.reloadData()
// reset le champs
ressourceTextField.text = ""
picker.selectRow(0, inComponent: 0, animated: true)
}
}
Here is some code I'm using. I'm thinking the problem came from the tableview Func
var arrayPickerValue: [String] = ["", "maison", "telephone"]
// ------------------------------ START PICKER ------------------------------------
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
print("-----arraypicker.count------")
print(arrayPickerValue.count)
return arrayPickerValue.count
}
// hauteur du picker pour que les images ne se supperpose pas / Picker height
func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
return 25
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
print("----arraypicker[row]----")
print(arrayPickerValue[row])
return arrayPickerValue[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
stored = arrayPickerValue[row]
print("-----stored dans func didselectrow------")
print(stored as Any)
}
//-------------------------- END PICKER -----------------------------------
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let row = indexPath.row
let value = arrayRessource[row]
cell.textLabel?.text = "- " + String(value) + " €"
// cell.detailTextLabel?.text =
updateRestLabel()
setRestLabel()
return cell
}
Here is my outlet :
#IBOutlet weak var tableRessourceOT: UITableView!
Then my arrayRessource :
var arrayRessource: [Int] = [] {
didSet {
if oldValue != arrayRessource {
userDefault.setValue(arrayRessource, forKey: arrayKey)
}
}
}
and call with the function :
func getArray() {
if let newArray = userDefault.array(forKey: arrayKey) as? [Int] {
arrayRessource = newArray
}
}
Your question is not very clear.
However I will make an attempt to offer some help from what I can extract from your code.
I assume restValueLabel is not something within your cells? It is probably the green label "2500" in your screenshot?
If that is the case, don't set it in cellForRow(at:). This method is called every time a cell becomes visible and should only be used to configure that very cell based on your data source (arrayRessource).
Instead create a method updateRestValueLabel() that iterates your data source, does the calculation and sets the appropriate value to your restValueLabel. Call that method every time your data source changes, so along with tableRessourceOT.reloadData().
There are also some stylistic issues with your code (e.g. avoid force unwrapping ! unless you have good reason to use it) but this is out of scope here.
The problem come from my array was an Int array and my pickerValue was String array.
Array can't be String array's and Int array's.
var arrayRessource: [String] = [] {
didSet {
if oldValue != arrayRessource {
userDefault.setValue(arrayRessource, forKey: arrayKey)
}
}
}
Thanx for your advice by the way i'm progressing and doing my best
I have encountered some synchronisation/graphic update problems with my UIPickerView.
I want a view with 2 components, where the content of the second component depends on the selected row of the first component.
My code is inspired from: Swift UIPickerView 1st component changes 2nd components data
However, while it seems to work, sometimes (not every time) there are some visual problems, as seen on the screenshots below. (on the second screenshot, you can see that the rows of the second component are not really correct, and are a mix of the rows from the first and the second component)
Here is the code:
import UIKit
class AddActivityViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var typePicker: UIPickerView!
var pickerData: [(String,[String])] = []
override func viewDidLoad() {
super.viewDidLoad()
self.typePicker.delegate = self
self.typePicker.dataSource = self
pickerData = [("sport",["bike", "run", "soccer", "basketball"]),
("games",["videogame", "boardgame", "adventuregame"])]
// not sure if necessary
typePicker.reloadAllComponents()
typePicker.selectRow(0, inComponent: 0, animated: false)
// pickerData = [("sport",["bike", "run", "soccer"]),
// ("games",["videogame", "boardgame", "adventuregame"])]
}
// number of columns in Picker
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 2
}
// number of rows per column in Picker
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
print("function 1 called")
if component == 0 {
return pickerData.count
} else {
let selectedRowInFirstComponent = pickerView.selectedRow(inComponent: 0)
return pickerData[selectedRowInFirstComponent].1.count
}
}
// what to show for a specific row (row) and column (component)
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
print("function 2 called with values: component: \(component), row: \(row)")
if component == 0 {
// refresh and reset 2nd component everytime another 1st component is chosen
pickerView.reloadComponent(1)
pickerView.selectRow(0, inComponent: 1, animated: true)
// return the first value of the tuple (so the category name) at index row
return pickerData[row].0
} else {
// component is 1, so we look which row is selected in the first component
let selectedRowInFirstComponent = pickerView.selectedRow(inComponent: 0)
// we check if the selected row is the minimum of the given row index and the amount of elements in a given category tuple array
print("---",row, (pickerData[selectedRowInFirstComponent].1.count)-1)
let safeRowIndex = min(row, (pickerData[selectedRowInFirstComponent].1.count)-1)
return pickerData[selectedRowInFirstComponent].1[safeRowIndex]
}
//return pickerData[component].1[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
// This method is triggered whenever the user makes a change to the picker selection.
// The parameter named row and component represents what was selected.
}
}
Is this a problem with my code or generally a complicated aspect of UIPickers that can not be trivially solved?
Additionally, is there a nicer way to develop this functionality?
I solved the error, however I do not understand why this solves it.
The solution is to imlement the func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int)method, which I did not believe to be necessary just to show the fields.
In other words, just add this to my existing code:
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if component == 0 {
pickerView.reloadComponent(1)
} else {
let selectedRowInFirstComponent = pickerView.selectedRow(inComponent: 0)
print(pickerData[selectedRowInFirstComponent].1[row])
}
}
I'm working with XCode 8.2.1, Swift 3 and iOS10.
I've a list of items with the following format:
ID | Name
---------
1 | John
2 | Maria
3 | Peter
4 | Roger
The code looks like this:
var formsList = [1:"John", 2:"Maria", 3:"Peter", 4:"Roger"]
What I want to do is to set that data into an UIPickerView, so when someone chooses for example John, the ID 1 is returned, or if someone chooses Peter, the ID 3 is returned.
I do other stuff once I get that ID, that's why I need it.
Any idea or suggestion on how I can achieve this?
Thanks!
You just need to sort your dictionary by its keys and use it as your picker data source:
let formsList = [1:"John", 2:"Maria", 3:"Peter", 4:"Roger"]
let dataSource = formsList.sorted{$0.key<$1.key}
This way you have all your dictionary names sorted in an array including their IDs. Your picker should look something like this:
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var pickerView: UIPickerView!
#IBOutlet weak var label: UILabel!
let formsList = [1:"John", 2:"Maria", 3:"Peter", 4:"Roger"]
var dataSource: [(key: Int, value: String)] = []
override func viewDidLoad() {
super.viewDidLoad()
dataSource = formsList.sorted{$0.key<$1.key}
pickerView.delegate = self
pickerView.dataSource = self
label.text = "id: " + String(dataSource[pickerView.selectedRow(inComponent: 0)].key) + " - " + "name: " + dataSource[pickerView.selectedRow(inComponent: 0)].value
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return dataSource.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return dataSource[row].value
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
label.text = "id: " + String(dataSource[row].key) + " - " + "name: " + dataSource[row].value
}
}
sample
Your approach of using a dictionary of your data structure is problematic. Dictionaries are, by design, unordered.
There are lots of ways to do this. Most involve creating an array of some sort containing items for each entry from which you want the user to pick.
For example, create an array of tuples:
typealias NameTuple = (id: Int, name: String)
var namesArray: [NameTuple]
Feed your picker view with the name field of each entry in your array. When the user selects an item, use the selected index to fetch that tuple and then get the ID.
You could also use an array of structs, or an array of name objects.
I hope the title makes some sense but what I am trying to do is to set a field value to the item selected variable from my dataPicker. I have been able to make this work when there is only one field to set but my project will have multiple fields on each view that will call data from the dataPicker based on what field it is. I hope that is clear. Maybe as you look at the code it will.
I have set up a test project to limit things to this issue only. So my variable to tell the view what array to populate in the dataPicker is either season or sport. the field that will receive the data from the season/sport array is enterSeason and enterSport. When the picker has returned a value from season, I want to combine it with enter to create the var enterSeason to set that == itemSelected. This language is very new to me so I am trying the only way I have used before to combine text and variables in one value. It is obviously not working. Help is appreciated.
import UIKit
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource, UIGestureRecognizerDelegate {
#IBOutlet var enterSeason: UITextField!
#IBOutlet var enterSport: UITextField!
var dataPickerView = UIPickerView()
var season = ["2013", "2014", "2015"] //multi-season
//var season = ["2015"] //single-season
var sport = ["Baeball", "Football", "Basketball", "Hokey"]
var activeField = []
override func viewDidLoad() {
super.viewDidLoad()
enterSeason.inputView = dataPickerView
dataPickerView.delegate = self
dataPickerView.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return activeField.count
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
return activeField[row] as! String
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
var itemSelected = activeField[row] as! String
self.enter"\activeField".text = itemSelected
}
}
EDIT : How do you show and hide the picker? Your code anly shows variable declarations and the delegate methods... answers could vary accordingly..
Since you show the picker as text field's input view, set UITextFieldDelegate for each of these text fields .. and in the textFieldDidBeginEditing check which field becomes active with simple if else
func textFieldDidBeginEditing(textField: UITextField) {
if textField === enterSeason {
activeField = season
}
else if textField === enterSport {
activeField = sport
}
}
And in the picker selector, set value of the relevant text field as per current activeField object
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if activeField === season {
enterSeason.text = season[row] as! String
}
else if activeField === sport {
enterSeason.text = sport[row] as! String
}
}
Setting the delegate for your text fields in storboard/xib :
P.S.
- Rename activeField to activeDataArray or somethiong more appropriate
EDIT 2 : As you mentioned, second approach i have mentioned below is not suitable for you because there are too many of these fields i am still keeping it as part of the answer as it may help someone else
But what you are trying to achieve is very simple and approach is too convoluted / weird. So heres another way you can implement the whole thing..
The easiest (but still probably not the best) way is to have two instances of the UIPickerView for each field. you can directly check pickerView == seasonPickerView OR pickerView == sportPickerViewin an if else block and do the conditional programming and you wont need the activeField variable..
just got some great help which made the UIPickerView wheels work perfectly but they I have been trying to get the data in the 2nd and 3rd components to change dependant on the position of the 1st component.
I can get through some println lines to work out that the variable that I pull out whatConversion has the correct value but I have no idea how to change the array and make the UIPickerView update with the new values.
Please help for my sanity and also I am going to have to put extra time in at work after spending nearly all day on these, what dozen lines of code.
Thanks in advance
Motty
import UIKit
class ViewController: UIViewController, UIPickerViewDelegate {
#IBOutlet weak var picker1Label: UILabel!
#IBOutlet weak var picker2Label: UILabel!
#IBOutlet weak var bigPicker: UIPickerView!
var wheelContents:[[String]] = []
var length = ["metres","feet","yards","inches","mm","cm","miles"]
var volume = ["m3","US Gall","Imp Gall","Barrels", "cubic FT","litres"]
var conType = ["length","volume"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
bigPicker.delegate = self
wheelContents = [conType, length, length]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//For main selection of type of conversion
// returns the number of 'columns' to display.
func numberOfComponentsInPickerView(bigPicker: UIPickerView) -> Int{
return wheelContents.count
}
// returns the # of rows in each component..
func pickerView(bigPicker: UIPickerView, numberOfRowsInComponent component: Int) -> Int{
return wheelContents[component].count
}
func pickerView(bigPicker: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String!{
return wheelContents[component][row]
}
func pickerView(bigPicker: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
var whatConversion = wheelContents[0][bigPicker.selectedRowInComponent(0)]
switch(whatConversion){
case "length":
wheelContents = [conType, length, length]
bigPicker.numberOfRowsInComponent(wheelContents[component].count)
break
case "volume":
wheelContents = [conType, volume, volume]
break
default:
break
}
}
}
I am even happier I managed to figure this out myself by looking at the Class definitions and functions
used bigPicker.reloadAllComponents()