I have a PickerView that deposit values. This values will be calculate with a user textfield input. The result will show in a label. That works for me.
By starting the app the textfield have a fix number of 1.
How can I now update my label automatically by use the Pickerview?
I read and try many things but it don´t works.
Maybe it is a little thing, but i don´t see it.
import UIKit
class BalkenbewehrungVC: UIViewController, UIPopoverPresentationControllerDelegate, UIPickerViewDataSource, UIPickerViewDelegate, UITextFieldDelegate {
// Deklarationen Pickerview
var multiplicator : Double = 0.0
let multiplicators = [0.0,6.0,8.0,10.0,12.0,14.0,16.0,20.0,25.0,26.0,28.0,30.0,32.0,36.0,40.0,50.0]
let PI = 3.1415
// Pickerview füllen
var pickerDataSource = ["---","Ø 6","Ø 8","Ø 10","Ø 12","Ø 14","Ø 16","Ø 20","Ø 25","Ø 26","Ø 28","Ø 30","Ø 32","Ø 36","Ø 40","Ø 50"]
// Picker im aktivieren
#IBOutlet weak var Pickerview: UIPickerView!
// Ausgabewert As = xxxx cm
#IBOutlet var AusgabePicker: UILabel!
// Eingabe Anzahl Stäbe
#IBOutlet var AnzahlStab: UITextField!
#IBAction func test(sender: UITextField) {
if AnzahlStab.text!.isEmpty {
AnzahlStab.text = "1"
let result1 = (multiplicator / 10) * (multiplicator / 10) * PI / 4 * 1
AusgabePicker.text! = String(format:"%.3f", result1)
} else {
let result = (multiplicator / 10) * (multiplicator / 10) * PI / 4 * (AnzahlStab.text! as NSString).doubleValue
// let resultString = "\(result)"
AusgabePicker.text = String(format:"%.3f", result)
}
}
// Picker - feste Reihen
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
// Picker - Aufaddierung neuer Reihen
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return pickerDataSource.count
}
// Picker - Datenübernahme multiplicator in Picker
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return pickerDataSource[row]
}
// Picker - Wertefestlegung Picker
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
multiplicator = multiplicators[row]
_ = NSNumberFormatter.localizedStringFromNumber(NSNumber(double: multiplicator), numberStyle:.DecimalStyle)
}
In the didSelectRow method for the pickerView, set the label's text to your newly calculated value.
Update: To update the label's text depending on the textField's input, inside your ViewController's viewDidLoad method, add this line
AnzahlStab.delegate = self
and then add the textFieldDidEndEditing method, and inside it update the label's text to the new value. This method is called when you are done with editing the text field.
Need to call function test in picker view didSelectRow. Or Simply Replace your didSelectRow with following code.
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
multiplicator = multiplicators[row]
_ = NSNumberFormatter.localizedStringFromNumber(NSNumber(double: multiplicator), numberStyle:.DecimalStyle)
self.test(AnzahlStab)
}
Hope this Answer will help you.
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])
}
}
In my picker view I want the default row to be zero. This row has a value of 1. I want to be able to touch nothing on the view contoller except a button. I know there are similar questions but they did not work for me.
override func viewWillAppear(_ animated: Bool) {
self.pickerView.delegate = self
self.pickerView.dataSource = self
self.pickerView.selectRow(0, inComponent: 0, animated: true)
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return String(numbers[row])
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return numbers.count
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
therow = pickerView.selectedRow(inComponent: 0) + 1
}
then
#IBAction func submitTapped(_ sender: Any) {
Print (therow)
}
When I tap submit and print the value at row 0 it is 0, but if I wiggle the picker view and put it back on row 0 then it prints 1. I need to be able to touch nothing on the picker view and have it return the proper value of the default row.
You should use the row that the pickerview delegate method gives you , so you should modify your code as follows:
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
therow = numbers[row]
//theRowIndex = row //this is the index of row that you selected
}
e.g if numbers array is numbers = [1, 2, 3, 4], when you click on first row above code will set therow to be 1 and if you click on second row, it will set therow to be 2 and so on.
if you want to use the code that you wrote then you can use as follows:
therow = numbers[pickerView.selectedRow(inComponent: 0)]
this will give you the number for selected row , but I think you dont need it inside the above method.
Now if you dont want to touch the picker then I think you need to do this:
#IBAction func submitTapped(_ sender: Any) {
therow = numbers[self.pickerView.selectedRow(inComponent: 0)]
print(therow)
}
Use this statement once you load your picker view with data.
yourPicker.selectRow(0, inComponent:0, animated:true)
You can change the default selected value by changing the first parameter of selectRow.
I think the reason why this happens is that didSelectRow is somehow not called if you selected the row programmatically. As per the docs:
Called by the picker view when the user selects a row in a component.
So you need to set your therow property programmatically after you call selectRow:
self.pickerView.selectRow(0, inComponent: 0, animated: true)
therow = 1 // <--- this line
Let's say I have a stream of dogs private var dogs: Observable<[Dogs]>. Every time a new value is produced my block is called where I create a new dataSource and delegate for my UIPickerView and then within the block I call pickerView.reloadAllComponents() but my view appears with an empty pickerView, even though the dataSource and delegate are queried.
Example code:
self.dataStream
.subscribeNext {
self.dataSource = PickerViewDataSource(data: $0)
self.pickerView.dataSource = self.dataSource
self.delegate = PickerViewDelegate(data: $0, selectedRow: self._selectedRowStream)
self.pickerView.delegate = self.delegate
self.pickerView.reloadAllComponents()
}.addDisposableTo(self.disposeBag)
Debugging the dataSource and delegate I know these are queried and the reason I am keep dataSource and delegate reference in the UIViewController is due to the fact that the UIPickerView holds a weak reference for these.
This one of the last strategies I have tried and still get the same result. Any help would be appreciated. Thanks.
Update:
DogPickerViewDataSource:
class DogPickerViewDataSource: NSObject, UIPickerViewDataSource {
private var dogs: [Dog]
init(
dogs: [Dog]
) {
self.dogs = dogs
}
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return dogs.count
}
}
DogPickerViewDelegate:
class DogPickerViewDelegate: NSObject, UIPickerViewDelegate {
private var selectedRow: BehaviorSubject<Int>
private var dogs: [Dog]
init(
dogs: [Dog],
selectedRow: BehaviorSubject<Int>
) {
self.dogs = dogs
self.selectedRow = selectedRow
}
func pickerView(pickerView: UIPickerView, attributedTitleForRow row: Int, forComponent component: Int) -> NSAttributedString? {
let dogName = (self.dogs[row].name)!
return NSAttributedString(string: dogName)
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
self.selectedRow.onNext(row)
}
}
If I understand you correctly, on viewDidLoad you're initialising your pickerView with new delegate and data source, calling reloadAllComponents and expecting that everything should be displayed properly. But you're still displaying pickerView from previous view controller. You should try to hide picker view on previous view controller's viewWillDisappear method and display new one in viewDidAppear method of new view controller.
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..