Calculations with UIPickerView and UITextField - swift

I am working on an app where the user inputs a textfield value and it is multiplied by a value stored in UIPickerView's didSelectRow method. This is currently working but only when I enter the value and touch/select the picker. How can I have the calculation performed against the selected row as soon as the value is entered into the textfield? Thanks.
class Example2ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource, UITextFieldDelegate {
#IBOutlet weak var inputField: UITextField!
#IBOutlet weak var resultsLabel: UILabel!
#IBOutlet weak var calcLabel: UILabel!
#IBOutlet weak var myPicker: UIPickerView!
var pickerData: [String] = [String]()
override func viewDidLoad() {
super.viewDidLoad()
myPicker.delegate = self
myPicker.dataSource = self
self.resultsLabel.text = ""
self.calcLabel.text = ""
pickerData = ["Item 1", "Item 2", "Item 3","Item 4", "Item 5", "Item 6"]
myPicker.selectRow(1, inComponent: 0, animated: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
print(string)
return true
}
#IBAction func editingChanged(sender: UITextField) {
sender.text = sender.text?.uppercaseString
inputField.text = sender.text
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?){
view.endEditing(true)
super.touchesBegan(touches, withEvent: event)
}
//UI Pickerview
// The number of columns of data
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
// The number of rows of data
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return pickerData.count
}
// The data to return for the row and component (column) that's being passed in
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return pickerData[row]
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
var multiplicator : Double = 1.0
if (row == 0)
{
resultsLabel.text = "Item 1"
multiplicator = 1.00
}
else if (row == 1)
{
resultsLabel.text = "Item 2"
multiplicator = 2.00
}
else if (row == 2)
{
resultsLabel.text = "Item 3"
multiplicator = 3.00
}
else if (row == 3)
{
resultsLabel.text = "Item 4"
multiplicator = 4.00
}
else if (row == 4)
{
resultsLabel.text = "Item 5"
multiplicator = 5.00
}
else if (row == 5)
{
resultsLabel.text = "Item 6"
multiplicator = 6.00
}
let result = multiplicator * (inputField.text! as NSString).doubleValue
calcLabel.text = "Results: \(result)"
}
}

I have tried something more simple. I made an #IBAction call of the UItextField when edit is done and then I make all my post picker selection there.
#IBAction func textField_pickerAction(_ sender: Any) {
let row = picker.selectedRow(inComponent: 0)
...
...
...
}

You can do this by making multiplicator a global var and then you can use it inside the textfield function:
var multiplicator:Double!
#IBAction func editingChanged(sender: UITextField) {
sender.text = sender.text?.uppercaseString
inputField.text = sender.text
let result = multiplicator * (inputField.text! as NSString).doubleValue
calcLabel.text = "Results: \(result)"
}
You can now obviously remove the last 2 lines of code inside the pickerview func

In addition to the global var, I got this working by adding the UIPickerView if statement to it's own function. Now I can call that anywhere I need to.
I'm am updating the calcLabel in real time from the inputField using this:
inputField.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)
func textFieldDidChange(textField: UITextField) {
pickerFunc()
}

Related

Passing data between Main VC to second VC doesn't work in Swift

Would you please help me? :) I've been struggling with this problem for like a week and couldn't solve it. my problem is - I can't pass data between my two view controllers, let me explain myself better:
I've got one VC which is the main VC that has the Clock animation, and I've got another VC which takes care of the SettingsVC. now no matter what I do (delegation, anything..) it doesn't let me to pass my UILabel's text to the main VC. I beg for a help since I can't keep going with my app and I'm about to finish it.
MyCode:
1st ViewController:
import UIKit
import CircleProgressView
import AVFoundation
import Foundation
import PickerViewCell
class PomodoroViewController: UIViewController{
//MARK: - Outlets Section:
#IBOutlet weak var playSymbol: UIButton!
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var settingsSymbol: UIBarButtonItem!
#IBOutlet weak var timeLabel: UILabel!
#IBOutlet weak var deleteTask: UIButton!
#IBOutlet weak var tasksLabel: UILabel!
#IBOutlet weak var circleProgressView: CircleProgressView!
// MARK: - Class Variables
private var timerIsOn = false
var timeRemaining = 1500
private var totalTime = 1500
private var completedTasks = 0
private var secondsPassed = 0 // AINT SURE
private var counter = 0
//Instance objects:
private var timer = Timer()
var calculateManager = CalculateManager()
//MARK:- ViewDidLoad Section:
override func viewDidLoad() {
textField.delegate = self
deleteTask.isHidden = true
}
//MARK: - Play Function:
#IBAction func playPreesedButton(_ sender: UIButton) {
//What will happen once we hit the play button:
counter += 1 //1 = play, 2 = pause, 3 = play, 4 = pause
if !timerIsOn
{ // means its true cause the user pressed the play button:
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(onTimerFires), userInfo: nil, repeats: true)
timerIsOn = true
deleteTask.isHidden = false // showing up the delete button.
}
playSymbol.setBackgroundImage(UIImage(named: T.P.pausePic), for: .normal) // setting the image to pause button
if counter.isMultiple(of: 2) {
timer.invalidate()
timerIsOn = false // cause we stopped the timer due to the Pause Button.
playSymbol.setBackgroundImage(UIImage(named: T.P.playPic), for: .normal) // setting the image to play button
}
}
//MARK: - Update Function:
#objc func onTimerFires()
{
print("the total time is - \(timeRemaining)")
timeRemaining -= 1 //1499 - in seconds
secondsPassed = totalTime - timeRemaining
//Updating the timeLabel:
let minutesLeft = Int(timeRemaining) / 60 % 60
let secondsLeft = Int(timeRemaining) % 60
let timeToDisply = String(format:"%02i:%02i", minutesLeft, secondsLeft)
timeLabel.text = timeToDisply
//Updating the progressBar View:
let cal = calculateManager.updateProgressView(seconds: secondsPassed, time: totalTime)
circleProgressView.setProgress( cal , animated: true)
if timeRemaining <= 0 {
//what happen once we finish the 25mins:
timer.invalidate()
//Updating the tasks:
if completedTasks < 10 {
completedTasks += 1
}
tasksLabel.text = "Tasks: \(completedTasks)/10"
//TODO: Play the sound that the user chose:
//Setting up the timer to 5mins break after an interval is done:
timeLabel.text = T.breakTime // Setting up the TimeLabel to be 5:00 of the break
timeRemaining = T.breakInMins // Setting up the totalTime to 300seconds which is 5 mins
playSymbol.setBackgroundImage(UIImage(named: T.P.playPic), for: .normal) // Setting up the Icon to the play button.
}
}
//MARK: - Reset Function:
#IBAction func resetTapped(_ sender: UIButton) {
let alert = UIAlertController(title: "Are you sure you want to skip this task?", message: .none, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Skip", style: .destructive, handler: { action in
//Once the user wants to reset the task - known as the Reset Button.
self.timer.invalidate()
self.timeRemaining = 1500
self.timeLabel.text = "25:00"
self.textField.text = "" //setting the textfield to be empty
self.timerIsOn = false
self.playSymbol.setBackgroundImage(UIImage(named: T.P.playPic), for: .normal)
self.circleProgressView.setProgress( T.resetProgress , animated: false)
}))
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
self.present(alert, animated: true)
}
//MARK: - Settings Section:
#IBAction func settingsPreesedButton(_ sender: UIButton) {
}
}
//MARK: - UITextField Delegate Methods:
extension PomodoroViewController: UITextFieldDelegate
{
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}
extension PomodoroViewController: CalculateManagerDelegate {
func updateTotalTime(time: Int) {
timeRemaining = time
}
}
my second ViewController:
import Foundation
import UIKit
import AVFoundation
import PickerViewCell
//MARK: - Settings View Controller:
class SettingsViewController: UITableViewController, PickerTableCellDelegate, PickerTableCellDataSource {
#IBOutlet weak var workInterval: UILabel!
#IBOutlet weak var shortBreak: UILabel!
#IBOutlet weak var longBreakAfter: UILabel!
#IBOutlet weak var dailyIntervals: UILabel!
let defaults = UserDefaults.standard
var calculateManager = CalculateManager()
override func viewDidLoad() {
checkForSavedWorkTime()
}
var timesDictionary = [0: "30 Minutes",
1: "25 Minutes",
2: "20 Minutes",
3: "15 Minutes",
4: "10 Minutes",
5: "5 Minutes"
]
var intervalsDictionary = [ 0: "1 Intervals",
1: "2 Intervals",
2: "3 Intervals",
3: "4 Intervals",
4: "5 Intervals",
5: "6 Intervals",
6: "7 Intervals",
7: "8 Intervals",
8: "9 Intervals",
9: "10 Intervals"
]
var totalWorkTime = 0
//MARK: Picker TableCell Delegate Methods:
//This func chooses the title of every row in the UIPickerView selection:
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int, forCell cell: PickerTableViewCell) -> String? {
let identifier = cell.reuseIdentifier
switch identifier {
case "timeCell":
return timesDictionary[row]
case "shortBreakCell":
return timesDictionary[row]
case "longBreakAfterCell":
return timesDictionary[row]
case "intervalsCell":
return intervalsDictionary[row]
default:
print("There was a problem with titleforrow")
}
return nil
}
//This func takes control of what will happen once the user choose an optaion in the UIPickerView Selection:
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int, forCell cell: PickerTableViewCell) {
let identifier = cell.reuseIdentifier
switch identifier {
case "timeCell":
workInterval.text = timesDictionary[row]
let myString = workInterval.text!
if let totalTime = calculateManager.calculateTotalTime(convertString: myString) {
totalWorkTime = totalTime
}
saveTime(labelText: myString)
case "shortBreakCell":
shortBreak.text = timesDictionary[row]
case "longBreakAfterCell":
longBreakAfter.text = timesDictionary[row]
default:
print("There was a problem with Switch")
}
self.view.endEditing(true)
}
func saveTime(labelText: String) {
defaults.set(labelText, forKey: "workInterval")
}
func checkForSavedWorkTime() {
if let time = defaults.string(forKey: "workInterval") {
workInterval.text = time
}
}
func onPickerOpen(_ cell: PickerTableViewCell) {
}
func onPickerClose(_ cell: PickerTableViewCell) {
}
//MARK: Picker TableCell DataSource:
func numberOfComponents(in pickerView: UIPickerView, forCell cell: PickerTableViewCell) -> Int {
return 1
}
//the amount of rows in component(UIPicker):
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int, forCell cell: PickerTableViewCell) -> Int {
if cell.reuseIdentifier == "timeCell"{
return timesDictionary.count
}
return intervalsDictionary.count
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
if let cell = tableView.cellForRow(at: indexPath) as? DatePickerTableViewCell {
// cell.delegate = self
if !cell.isFirstResponder {
_ = cell.becomeFirstResponder()
}
} else if let cell = tableView.cellForRow(at: indexPath) as? PickerTableViewCell {
cell.delegate = self
cell.dataSource = self
if !cell.isFirstResponder {
_ = cell.becomeFirstResponder()
}
}
}
}
//MARK: - Completed View Controller:
class CompletetedSoundViewController: UITableViewController {
/// Class Variables
private var soundsArray = [Track]()
override func viewDidLoad() {
// tableView.delegate = self
// tableView.dataSource = self
}
///TableView Data Soruce Methods:
// Return the number of rows for the table.
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return soundsArray.count
}
// Provide a cell object for each row.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Fetch a cell of the appropriate type.
tableView.deselectRow(at: indexPath, animated: true)
let cell = tableView.dequeueReusableCell(withIdentifier: "trackCell", for: indexPath)
// Configure the cell’s contents.
cell.textLabel!.text = soundsArray[indexPath.row].trackName
return cell
}
///TableView Delegate Methods:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
}
//MARK: - Break Sound Controller:
class BreakSound: UITableViewController {
override func viewDidLoad() {
//
}
}
my Struct:
import Foundation
import UIKit
protocol CalculateManagerDelegate {
func updateTotalTime(time: Int)
}
struct CalculateManager {
var totalAmountOfTime = 0
var delegate: CalculateManagerDelegate?
//TODO: Calculate the Progress-View:
func updateProgressView(seconds: Int, time: Int ) -> Double {
return Double(seconds) / Double(time)
}
mutating func calculateTotalTime(convertString: String) -> Int? {
let newString = convertString.dropLast(8)
let subToString = String(newString)
if let turnToInt = Int(subToString)
{
totalAmountOfTime = turnToInt * 60
delegate?.updateTotalTime(time: totalAmountOfTime)
return totalAmountOfTime
}
return nil
}
}
first declare the delegate:
protocol SettingsDelegate {
func userSettings(interval:String?,shortBreak:String?,longBreak:String?)
}
then inside I would declare 3 strings (or whatever you want to pass back) and the delegate:
class SettingsViewController: UIViewController {
// this variables are optionals but it would be a good idea to set the initial value, maybe from UserSettings
var workIntervalString: String?
var shortBreakString: String?
var longBreakAfterString: String?
// your delegate
var delegate: SettingsDelegate?
// the inside the didSelectRow set the string variables
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int, forCell cell: PickerTableViewCell) {
let identifier = cell.reuseIdentifier
switch identifier {
case "timeCell":
workInterval.text = timesDictionary[row]
self.workInterval = timesDictionary[row] // set string
let myString = workInterval.text!
if let totalTime = calculateManager.calculateTotalTime(convertString: myString) {
totalWorkTime = totalTime
}
saveTime(labelText: myString)
case "shortBreakCell":
shortBreak.text = timesDictionary[row]
self.shortBreakString = timesDictionary[row] // set string
case "longBreakAfterCell":
longBreakAfter.text = timesDictionary[row]
self.longBreakAfterString = timesDictionary[row] // set string
default:
print("There was a problem with Switch")
}
// use your delegate
self.delegate?.userSettings(interval: self.workIntervalString,shortBreak:self.shortBreakString ,longBreak:self.longBreakAfterString)
self.view.endEditing(true)
}
in your PomodoroViewController:
class PomodoroViewController: UIViewController, SettingsDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func userSettings(interval: String?, shortBreak: String?, longBreak: String?) {
guard let interval = interval else {return}
guard let shortBreak = shortBreak else {return}
guard let longBreak = longBreak else {return}
//User your variables .......
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "your identifier" {
let destination = segue.destination as! SettingsViewController
destination.delegate = self // set the delefate as self
}
}

TextField return nil if never reselected

In my EditVC, I performed segue from selected CellForRow At indexPath to editVC.
In Edit VC. I want to expect results of data return as it is IF there is no modification. However, issue is if I did not rechoose/reselect the row at pickerView again, it will auto return nil.
I want to have results of if User did not modify the data/specific data only, other data will remain the same.
This is the results of print statement.
// This results is perform correctly for what I want IF I RESELECT THE PICKERVIEW IN TEXTFIELD BUT WILL AMEND MY CATEGORY DATABASE AS WELL.
currentTransaction.categoryID : Optional(9)
selectedCategory.categoryID : 9
currentTransaction.categoryTypeID : Optional(1)
selectedCategory.categoryTypeID : 1
currentTransaction.categorytype.name = Optional("EXPENSE 9")
selectedCategory.name = EXPENSE 9
// If I never reselect the pickerview in textfield
currentTransaction.categoryID : Optional(0) /* Suppose to be 9 */
selectedCategory.categoryID : 0 /* Suppose to be 9 */
currentTransaction.categoryTypeID : Optional(0) /* Suppose to be 1 */
selectedCategory.categoryTypeID : 0 /* Suppose to be 1 */
currentTransaction.categoryType.name = Optional("") /* Suppose to be EXPENSE 9 */
selectedCategory.name = /* Suppose to be EXPENSE 9 */
class EditTransactionTableViewController: UITableViewController {
let realm = try! Realm()
var selectedTransaction : Transaction? //selectedTransaction From TransactionVC's CellForRow
var categories : Results<Category>!
var currentCategories : Results<Category>!
var selectedCategory = Category()
#IBOutlet weak var amountTF: UITextField!
//PickerView for keyboard
lazy var pickerView : UIPickerView = UIPickerView()
//Segment Control
#IBOutlet weak var categorySCoutlet: UISegmentedControl!
#IBOutlet weak var categoryTF: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
amountTF.text = selectedTransaction?.amount
categoryTF.text = selectedTransaction?.categoryType?.name
categorySCoutlet.selectedSegmentIndex = selectedTransaction?.categoryType?.categoryTypeID as! Int
loadData()
setupPicker()
}
#IBAction func categoryTypeSC(_ sender: UISegmentedControl) {
guard let type = CategoryType(id: sender.selectedSegmentIndex) else {
fatalError("error")
}
currentCategories = categories.filter("categoryTypeID == %#", type.rawValue)
categoryTF.text = currentCategories.first?.name
pickerView.reloadAllComponents()
// pickerView.selectRow(0, inComponent: 0, animated: true)
}
//MARK:- Edit Transaction Btn
#IBAction func editTransactionButtonTapped(_ sender: UIButton) {
if let currentTransaction = selectedTransaction {
try! realm.write {
currentTransaction.amount = amountTF.text!
//Success modification if got RESELECT row of category BUT will return "" if never reselect and cause categories list become chosen categoryID, name , and type
currentTransaction.categoryType?.categoryID = selectedCategory.categoryID
currentTransaction.categoryType?.categoryTypeID = selectedCategory.categoryTypeID
currentTransaction.categoryType!.name = selectedCategory.name
print("currenttransaction.categoryID : \(currentTransaction.categoryType?.categoryID)")
print("selectedCategory.categoryID : \(selectedCategory.categoryID)")
print("currenttransaction.categoryTypeID : \(currentTransaction.categoryType?.categoryTypeID)")
print("selectedCategory.categoryTypeID : \(selectedCategory.categoryTypeID)")
print("currenttranasction.categorytype.name = \(currentTransaction.categoryType?.name)")
print("selectedcategory.name = \(selectedCategory.name)")
}
}
DataManager.shared.transVC.tableView.reloadData()
dismiss(animated: true, completion: nil)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
if touches.first?.view == view {
categoryTF.resignFirstResponder()
}
}
// MARK: - Data Manipulation
func loadData() {
categories = realm.objects(Category.self)
}
}
//MARK:- UIPICKERVIEW DELEGATE
extension EditTransactionTableViewController : UIPickerViewDelegate, UIPickerViewDataSource {
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return currentCategories.count
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return currentCategories[row].name
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
categoryTF.text = currentCategories[row].name
selectedCategory = currentCategories[row]
}
//MARK:- Picker Helper
func setupPicker() {
// currentCategories = categories.filter("categoryTypeID == %#", CategoryType.income.rawValue)
currentCategories = categories.filter("categoryTypeID == %#", selectedTransaction?.categoryType?.categoryTypeID)
categoryTF.inputView = pickerView
pickerView.delegate = self
pickerView.dataSource = self
categorySCoutlet.setTitle("Income", forSegmentAt: 0)
categorySCoutlet.setTitle("Expense", forSegmentAt: 1)
categorySCoutlet.addTarget(self, action: #selector(categoryTypeSC(_:)), for: .valueChanged)
}
}

Cannot subscript a value of type '[Double]' with an argument of type 'Double'

I would like to pass an array of a selected number as a Double However i get this error Cannot subscript a value of type '[Double]' with an argument of type 'Double'
Coud someone tell me what I am doing wrong? Thanks in Advance!
import UIKit
class SelectAttributesViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource, UITextFieldDelegate {
#IBOutlet weak var kitPickerView: UIPickerView!
#IBOutlet weak var reactionVolumeLabel: UILabel!
#IBOutlet weak var numberOfSampleTextField: UITextField!
#IBOutlet weak var volumeOfTemplateDNATextField: UITextField!
#IBOutlet weak var slider: UISlider!
#IBOutlet weak var calculateButton: UIButton!
let kits = ["kit 1"]
let pArray: [Double] = [10,20,50]
var currentValue = Int()
override func viewDidLoad() {
super.viewDidLoad()
self.numberOfSampleTextField.delegate = self
self.volumeOfTemplateDNATextField.delegate = self
kitPickerView.dataSource = self
kitPickerView.delegate = self
//slider.addTarget(self, action: "reactionVolumeSlider", for: .valueChanged)
let tap = UITapGestureRecognizer(target: self.view, action: #selector(UIView.endEditing(_:)))
view.addGestureRecognizer(tap)
// Do any additional setup after loading the view.
}
func pcrSliderInterval() {
slider.minimumValue = 0
slider.maximumValue = Float(pArray.count)
slider.isContinuous = false
}
#IBAction func reactionVolumeSlider(_ sender: UISlider) {
currentValue = pArray[Double(sender.value)] // This is where I get the error
reactionVolumeLabel.text = "\(currentValue) μL"
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return kits.count
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if row == 0{ print(kits[row])
}
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return kits[row]
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let oldText = textField.text, let r = Range(range, in: oldText) else {
return true
}
let newText = oldText.replacingCharacters(in: r, with: string)
let isNumeric = newText.isEmpty || (Double(newText) != nil)
let numberOfDots = newText.components(separatedBy: ".").count - 1
let numberOfDecimalDigits: Int
if let dotIndex = newText.firstIndex(of: ".") {
numberOfDecimalDigits = newText.distance(from: dotIndex, to: newText.endIndex) - 1
} else {
numberOfDecimalDigits = 0
}
return isNumeric && numberOfDots <= 1 && numberOfDecimalDigits <= 2
}
func createAlert (title: String, message: String){
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertAction.Style.default, handler: { (Action) in
alert.dismiss(animated: true, completion: nil)
}))
self.present(alert, animated: true, completion: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let masterMix = segue.destination as! MasterMixTableViewController
masterMix.numberOfSamples = numberOfSampleTextField.text!
masterMix.volumeOfTemplateDna = volumeOfTemplateDNATextField.text!
masterMix.reactionVolumeInt = currentValue
//masterMix.waterVolumeText = getWaterVolumeLabelText()
// let masterMix = segue.destination as! MasterMixTableViewController
// masterMix.loadViewIfNeeded()
// masterMix.numberOfSamples = String(numberOfSampleTextField.text)
// masterMix.waterVolumeLabel!.text = String(Double(numberOfSampleTextField.text!)! * 50
// masterMix.bufferVolumeLabel.text = String(Double(numberOfSampleTextField.text!)! * 10)
// masterMix.dNTPsVolumeLabel.text = numberOfSampleTextField!.text // done
// masterMix.forwardPrimerLabel.text = String(Double(numberOfSampleTextField.text!)! * 2.5) //done
// masterMix.reversePrimerLabel.text = String(Double(numberOfSampleTextField.text!)! * 2.5) //done
// masterMix.dnaPolymeraseLabel.text = String(Double(numberOfSampleTextField.text!)! * 0.5) //done
//answer.text = String(Int(entry.text)! * 2)
}
#IBAction func calculatePressed(_ sender: Any) {
if numberOfSampleTextField.text != ""{
performSegue(withIdentifier: "masterMixSegue", sender: self)
}
}
}
The error says that an array cannot be subscripted by a Double.
The index type of Array is Int
pArray[Int(sender.value)]
And be aware that your maximum value will cause an out-of-range crash.
You cannot say
pArray[Double(sender.value)]
You need an Int. You would need
pArray[Int(sender.value)]
Except that that is unlikely to get you the desired index and you will probably crash at runtime. You will need to calculate the right index. I have no idea what you think is the relationship between your continuous slider values and the discrete indexes of an array, so I can’t say more. Maybe a slider was the wrong choice of interface?

How to use one pickerview for multiple textfield?

I am trying to build something (I am new to Xcode and swift) where I can choose a meal for a day from an array with Picker View, and I want my choice to display in that specific day's textfield. I have got this working, but for 1 day only. How can I get this same function working for all (7) days?
I managed to get the picker view when click on next textfield as well (Tuesday), but as I choose from the list the Mondays textfield will follow what I am doing for Tuesday. They are mirrored. I do get that I should probably make something to get thatTuesday-Picker unique somehow, but that's where Im stuck. I don't know what to change/write. Anyone with any ideas? I have googled around and find a lot regarding picker views but nothing for how they can been used in this specific way...
//
// ViewController.swift
// dropdown
//
// Created by -- on 2019-08-26.
// Copyright © 2019 --. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var monday: UITextField!
#IBOutlet weak var tuesday: UITextField!
#IBOutlet weak var wednesday: UITextField!
#IBOutlet weak var thursday: UITextField!
#IBOutlet weak var friday: UITextField!
#IBOutlet weak var saturday: UITextField!
#IBOutlet weak var sunday: UITextField!
// the menu
let Menu = ["Palak Paner",
"Spagetti Köttfärssås",
"Thai Haloumi",
"Thai Quorn",
"Linssoppa",
"SparrisPasta",
"Gröt",
"Gulasch"]
let tuesdayMenu = ["Palak Paner",
"Spagetti Köttfärssås",
"Thai Haloumi",
"Thai Quorn",
"Linssoppa",
"SparrisPasta",
"Gröt",
"Gulasch"]
//When a menu from the list is selected, it will be shown as a string
var mondaySelectedMenu: String?
var tuesdaySelectedMenu: String?
override func viewDidLoad() {
super.viewDidLoad()
//Call on these functions when loaded
createMondayMenuPicker()
createTuesdayMenuPicker()
createToolbar()
}
// This is the pickerView
func createMondayMenuPicker() {
let mondayMenuPicker = UIPickerView()
mondayMenuPicker.delegate = self
monday.inputView = mondayMenuPicker
}
func createTuesdayMenuPicker() {
let tuesdayMenuPicker = UIPickerView()
tuesdayMenuPicker.delegate = self
tuesday.inputView = tuesdayMenuPicker
}
// This is the "DONE" button
func createToolbar() {
let toolBar = UIToolbar()
toolBar.sizeToFit()
let doneButton = UIBarButtonItem(title: "DONE", style: .plain, target: self, action: #selector(ViewController.dismissKeyboard))
toolBar.setItems([doneButton], animated: false)
toolBar.isUserInteractionEnabled = true
monday.inputAccessoryView = toolBar
tuesday.inputAccessoryView = toolBar
}
#objc func dismissKeyboard() {
view.endEditing(true)
}
}
// This is the details for the pickerView
extension ViewController: UIPickerViewDelegate, UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return Menu.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return Menu[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
mondaySelectedMenu = Menu[row]
tuesdaySelectedMenu = tuesdayMenu[row]
monday.text = mondaySelectedMenu
tuesday.text = tuesdaySelectedMenu
}
}
So, I want to call on that same array when click on all seven days but I want to display the unique choices for everyday chosen from that list. Any ideas? Thanks a lot!
One way to achieve this is to do using the below approach. I have separated out toolbar functionality into its own class.
Create a new class pickerview's toolbar (in this case i have called ToolbarPickerView.swift)
import UIKit
protocol ToolbarPickerViewDelegate: class {
func didTapDone()
func didTapCancel()
}
class ToolbarPickerView: UIPickerView {
public private(set) var toolbar: UIToolbar?
public weak var toolbarDelegate: ToolbarPickerViewDelegate?
override init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.commonInit()
}
private func commonInit() {
let toolBar = UIToolbar()
toolBar.barStyle = UIBarStyle.default
toolBar.isTranslucent = true
toolBar.tintColor = .black
toolBar.sizeToFit()
let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(self.doneTapped))
let spaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let cancelButton = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(self.cancelTapped))
toolBar.setItems([cancelButton, spaceButton, doneButton], animated: false)
toolBar.isUserInteractionEnabled = true
self.toolbar = toolBar
}
#objc func doneTapped() {
self.toolbarDelegate?.didTapDone()
}
#objc func cancelTapped() {
self.toolbarDelegate?.didTapCancel()
}
}
In ViewController
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var monday: UITextField!
#IBOutlet weak var tuesday: UITextField!
#IBOutlet weak var wednesday: UITextField!
#IBOutlet weak var thursday: UITextField!
#IBOutlet weak var friday: UITextField!
#IBOutlet weak var saturday: UITextField!
#IBOutlet weak var sunday: UITextField!
var daysArray = [UITextField]()
let pickerView = ToolbarPickerView()
let Menu = ["Palak Paner",
"Spagetti Köttfärssås",
"Thai Haloumi",
"Thai Quorn",
"Linssoppa",
"SparrisPasta",
"Gröt",
"Gulasch"]
var selectedMenu : String?
override func viewDidLoad() {
super.viewDidLoad()
setupDelegateForPickerView()
setupDelegatesForTextFields()
}
func setupDelegatesForTextFields() {
//appending textfields in an array
daysArray += [monday, tuesday, wednesday, thursday, friday, saturday, sunday]
//using the array to set up the delegates, inputview for pickerview and also the inputAccessoryView for the toolbar
for day in daysArray {
day.delegate = self
day.inputView = pickerView
day.inputAccessoryView = pickerView.toolbar
}
}
func setupDelegateForPickerView() {
pickerView.dataSource = self
pickerView.delegate = self
pickerView.toolbarDelegate = self
}
}
Create an extension for textfield delegate
extension ViewController : UITextFieldDelegate {
func textFieldDidBeginEditing(_ textField: UITextField) {
self.pickerView.reloadAllComponents()
}
}
Extension for pickerview and toolbar
extension ViewController : UIPickerViewDelegate, UIPickerViewDataSource {
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return self.Menu.count
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return self.Menu[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
// Check if the textfield isFirstResponder.
if monday.isFirstResponder {
monday.text = self.Menu[row]
} else if tuesday.isFirstResponder {
tuesday.text = self.Menu[row]
} else if wednesday.isFirstResponder {
wednesday.text = self.Menu[row]
} else if thursday.isFirstResponder {
thursday.text = self.Menu[row]
} else if friday.isFirstResponder {
friday.text = self.Menu[row]
} else if saturday.isFirstResponder {
saturday.text = self.Menu[row]
} else if sunday.isFirstResponder {
sunday.text = self.Menu[row]
} else {
//log errors
}
}
}
extension ViewController: ToolbarPickerViewDelegate {
func didTapDone() {
// let row = self.pickerView.selectedRow(inComponent: 0)
// self.pickerView.selectRow(row, inComponent: 0, animated: false)
// selectedMenu = self.Menu[row]
self.view.endEditing(true)
}
func didTapCancel() {
self.view.endEditing(true)
}
}
PickerView's didSelectRow function can be simplified by changing it to below
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
for day in daysArray {
if day.isFirstResponder {
day.text = self.Menu[row]
}
}
}
Hopefully this answer will help you.
Create a common picker class like below:
class PKMultiPicker: UIPickerView, UIPickerViewDelegate, UIPickerViewDataSource {
internal typealias PickerDone = (_ firstValue: String, _ secondValue: String) -> Void
private var doneBlock : PickerDone!
private var firstValueArray : [String]?
private var secondValueArray = [String]()
static var noOfComponent = 2
class func openMultiPickerIn(_ textField: UITextField? , firstComponentArray: [String], secondComponentArray: [String], firstComponent: String?, secondComponent: String?, titles: [String]?, toolBarTint: UIColor = UIColor.black, doneBlock: #escaping PickerDone) {
let picker = PKMultiPicker()
picker.doneBlock = doneBlock
picker.openPickerInTextField(textField, firstComponentArray: firstComponentArray, secondComponentArray: secondComponentArray, firstComponent: firstComponent, secondComponent: secondComponent, toolBarTint: toolBarTint)
if titles != nil {
let label = UILabel(frame: CGRect(x: UIScreen.main.bounds.size.width/4 - 10, y: 0, width: 100, height: 30))
label.text = titles![0].uppercased()
label.font = UIFont.boldSystemFont(ofSize: 18)
picker.addSubview(label)
if PKMultiPicker.noOfComponent > 1 {
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 30))
label.text = titles![1].uppercased()
label.font = UIFont.boldSystemFont(ofSize: 18)
picker.addSubview(label)
} else {
label.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: 30)
label.textAlignment = NSTextAlignment.center
}
}
}
private func openPickerInTextField(_ textField: UITextField?, firstComponentArray: [String], secondComponentArray: [String], firstComponent: String?, secondComponent: String?, toolBarTint: UIColor = UIColor.black) {
firstValueArray = firstComponentArray
secondValueArray = secondComponentArray
self.delegate = self
self.dataSource = self
let cancelButton = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(pickerCancelButtonTapped))
cancelButton.tintColor = toolBarTint
let doneButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.done, target: self, action: #selector(pickerDoneButtonTapped))
doneButton.tintColor = toolBarTint
let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action:nil)
let toolbar = UIToolbar()
toolbar.sizeToFit()
let array = [cancelButton, spaceButton, doneButton]
toolbar.setItems(array, animated: true)
toolbar.backgroundColor = UIColor.lightText
textField?.inputView = self
textField?.inputAccessoryView = toolbar
let index = self.firstValueArray?.index(where: {$0.lowercased() == (firstComponent ?? "").lowercased() })
self.selectRow(index ?? 0, inComponent: 0, animated: true)
if PKMultiPicker.noOfComponent > 1 {
let index1 = self.secondValueArray.index(where: {$0.lowercased() == (secondComponent ?? "").lowercased() })
self.selectRow(index1 ?? 0, inComponent: 1, animated: true)
}
}
#IBAction private func pickerCancelButtonTapped(){
UIApplication.shared.keyWindow?.endEditing(true)
}
#IBAction private func pickerDoneButtonTapped(){
UIApplication.shared.keyWindow?.endEditing(true)
let index1 : Int?
let firstValue : String?
index1 = self.selectedRow(inComponent: 0)
if firstValueArray?.count == 0{return}
else{firstValue = firstValueArray?[index1!]}
var index2 :Int!
var secondValue: String!
if PKMultiPicker.noOfComponent > 1 {
index2 = self.selectedRow(inComponent: 1)
secondValue = secondValueArray[index2]
}
self.doneBlock((firstValue ?? ""), (secondValue ?? ""))
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if component == 0 {
return firstValueArray!.count
}
return secondValueArray.count
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return PKMultiPicker.noOfComponent
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
switch component {
case 0:
return firstValueArray?[row]
case 1:
return secondValueArray[row]
default:
return ""
}
}
}
Example Code to open picker and manage selection, there are some more options, you can manage number of components also:-
PKMultiPicker.noOfComponent = 1
PKMultiPicker.openMultiPickerIn(textField, firstComponentArray: ["Apple", "Mango","Grapes","Pine apple"], secondComponentArray: [], firstComponent: textField.text, secondComponent: nil, titles: nil, toolBarTint: AppColors.themeGreen) { (firstSelect, secondSelect) in
print("first select : \(firstSelect)")
textField.text = firstSelect // you can set text here to the respective text field.
}
Here #pawan_kumar has described a way for using same picker view for multiple textfields with same data. but you can bind different data sources for that as well. https://stackoverflow.com/a/60631018/10505343 has a sample code for that. here I use UITextField.isFirstResponder to decide what data should be loaded to the picker view. Hope this answer also will be helpful for you.

Multiple Pickerviews

I want to build a viewcontroller which contains three pickerviews (which has three same options). I am having problems on how to show one specific row to the label. My code:
I changed other parts still getting errors. Edited variables' first letters to lowercase.
#IBOutlet weak var picker1: UIPickerView!
#IBOutlet weak var picker2: UIPickerView!
#IBOutlet weak var picker3: UIPickerView!
var Array = ["Shake","Swipe Up","Swipe Right"]
#IBOutlet weak var label1: UILabel!
#IBOutlet weak var label2: UILabel!
#IBOutlet weak var label3: UILabel!
var placementAnswer = 0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
picker1.delegate = self
picker1.dataSource = self
picker1.tag = 1
picker2.delegate = self
picker2.dataSource = self
picker2.tag = 2
picker3.delegate = self
picker3.dataSource = self
picker3.tag = 3
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return Array[row]
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return Array.count
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
#IBAction func save3(_ sender: AnyObject) {
}
#IBAction func save2(_ sender: AnyObject) {
}
#IBAction func save1(_ sender: AnyObject) {
if (placementAnswer == 0){
label1.text = "Shake"
}
else if(placementAnswer == 1){
label1.text = "Swipe Up"
}
else{
label1.text = "Swipe Right"
}
if (placementAnswer == 0){
label2.text = "Shake"
}
else if(placementAnswer == 1){
label2.text = "Swipe Up"
}
else{
label2.text = "Swipe Right"
}
if (placementAnswer == 0){
label3.text = "Shake"
}
else if(placementAnswer == 1){
label3.text = "Swipe Up"
}
else{
label3.text = "Swipe Right"
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
picker1.delegate = self
picker1.dataSource = self
picker1.tag = 1
picker2.delegate = self
picker2.dataSource = self
picker1.tag = 2
picker3.delegate = self
picker3.dataSource = self
picker1.tag = 3
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if pickerView.tag == 1 {
// do things for pickerview 1
label1.text = row
}
else if pickerView.tag == 2 {
// do things for pickerview 2
label2.text = row
}else {
// do for pickerview 3
}
placementAnswer = row
}
}
Differentiate by using tag
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
picker1.delegate = self
picker1.dataSource = self
picker1.tag = 1
picker2.delegate = self
picker2.dataSource = self
picker1.tag = 2
picker3.delegate = self
picker3.dataSource = self
picker1.tag = 3
}
so in your delegate methods just check the tag
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return Array[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if pickerView.tag == 1 {
// do things for pickerview 1
Label1.text = Array[row]
}
else if pickerView.tag == 2 {
// do things for pickerview 2
Label2.text = Array[row]
}else {
// do for pickerview 3
}
}
Here you are # h44f33z. How I can make each section dependent with each other? for example, if i choose "shake" for behavior name 1, it wont be available for behavior name 2 and 3. Each behavior name section will have one corresponding action. thanks!