Update UITextField with returned value

I am new to xcode and attempting to make a simple equation calculator, consisting of 3 UITextFields which allows the user to enter a number into any of the fields and recieve an output in the other two fields.


However, when I enter a value for the "frequencyField.text" and press the calculate button, the correct answer is displayed in the "waveLengthField" but it prints as 0. The next time I press calculate it treats "length" as 0 as there is no user entered text. I would like the waveLengthField to update with the returned value "resultWave".


Here's the code:


@IBAction func Calculate(_ sender: UIButton) {

var freq = Float(frequencyField.text!) ?? 0

var length = Float(waveLengthField.text!) ?? 0

let speed = Float(343.0);

let resultWave = "\(speed / freq) m"

let resultFreq = speed / length


print(length)

print(freq)

print(speed)

frequencyField.text = String(resultFreq)

waveLengthField.text = String(resultWave)

}


Thanks

Accepted Reply

This should work.

Problem was a possible zero div crash, when field was empty


import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var frequencyField: UITextField!
    @IBOutlet weak var waveLengthField: UITextField!
    @IBOutlet weak var timePeriodField: UITextField!

    var freq = Float(0);
    var length = Float(0);

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }


    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func Calculate(_ sender: UIButton) {

        freq = Float(frequencyField.text!) ?? 1.0    
        length = Float(waveLengthField.text!) ?? 1.0
        print(freq)          // Just to see when they are 0
        print(length)
        let speed = Float(343.0)
        if freq > 0 {     // Avoid a zero div in some cases
             let resultWave = Float(speed / freq)
             waveLengthField.text! = String(resultWave)
       } else {
             waveLengthField.text! = "No freq defined"
      }
        if length > 0 {     // Avoid a zero div in some cases
          let resultFreq = Float (speed / length)   //   I have commented out these two lines, but this is the second part of the equation.
          frequencyField.text! = String(resultFreq)
       } else {
             frequencyField.text! = "No length defined"
      }

     // WHY would you need this ?
        //frequencyField.text! = "\(resultFreq)"
       // waveLengthField.text! = String(resultWave)

    }

}

Replies

In your present code:

   @IBAction func Calculate(_ sender: UIButton) {
 
        var freq = Float(frequencyField.text!) ?? 0
        var length = Float(waveLengthField.text!) ?? 0
        let speed = Float(343.0);
        let resultWave = "\(speed / freq) m"
        let resultFreq = speed / length

        print(length)
        print(freq)
        print(speed)
     
        frequencyField.text = String(resultFreq)
        waveLengthField.text = String(resultWave)
}

resultWave is a String, with non only numeric value


So, when you reuse on line 04

var length = Float(waveLengthField.text!) ?? 0


This String (eg "5.02 m") cannot be converted to Float, hence you get 0.


You have several ways to solve it:

1. Option (the simplest)

- create a Label at the right of waveLengthField (set constraints so that it is properly positioned), to hold the unit (m)

- you can in fact do it for all textFields

replace line 05 to get a Float

        let resultWave = speed / freq


2. Option (more complex)

Create var in your class to hold the values

var freq = Float(0)

var length = Float(0)


On lines 03 and 04, do not declare new var but use the already declared

       freq = Float(frequencyField.text!) ?? 0
       length = Float(waveLengthField.text!) ?? 0


Then when you set text in the corresponding textField, store the value in those var.

Select the textField in IB ; open its connections inspector ; connect an IBAction to the EditingChanged event

For instance, for freqField


     @IBAction func editingFreqField(_ sender: UITextField) {
          if Float(sender.text!) != nil {
              freq = Float(sender.text!)
          } else {
               freq = 0.0
               sender.text = "no value"
          }
     }


Create another IBAction when editingEnd:

Select the textField in IB ; open its connections inspector ; connect an IBAction to the EditingDidEnd and DidEndOnExit events

For instance, for freqField

     @IBAction func endEditingFreqField(_ sender: UITextField) {
          if Float(sender.text!) != nil {
               sender.text = "no value"
               let resultWave = "\(speed / freq) m"
              waveLengthField.text = String(resultWave)
          } else {
               freq = 0.0
               waveLengthField.text = "no value"
         }
     }

Thanks for the reply. I tried the first option but still got "inf" in the frequency text field. I then tried the 2nd option but keep getting this error "cannot assign value of type string to type float" when I attempt to assign the the sender text to variable "length". I can see the numbers printing when I type into the wavelength texfield, but can't seem to unwrap / assign them to a var / float.


@IBAction func waveLengthEdited(_ sender: UITextField) {

print(sender.text!) // I can see the correct numbers printing when I type. But not when I return them from the calculation, it // always returns to 0.

length = (sender.text!)

if Float(sender.text!) != nil {

length = (sender.text!)

} else {

freq = 0.0

sender.text = "no value"

}

}

@IBAction func Calculate(_ sender: UIButton) {

let speed = Float(343.0);

let resultWave = speed / freq

let resultFreq = speed / length


frequencyField.text! = "\(resultFreq)"

waveLengthField.text! = "\(resultWave)"

}

It is a bit difficult without getting the complete code.


But for the first option, of course you need as in previous post (sorry I edited rapidly the code):

       freq = Float(frequencyField.text!) ?? 1.0 
       length = Float(waveLengthField.text!) ?? 1.0

The same explanation as last time.


Does it work now (First option is much simpler for you now, I recommend to use it).


For the second option, you should implement completely what I proposed, noy just one IBAction.

But forget it for the moment, too complex for the beginning.

Just to confirm, is this the first option implemented correctly? I have added freq and length as variables in the class. Currently I can type into frequencyField and have it return the correct answer in waveLengthField. However, if I try and type anything in waveLengthField directly, the app crashes. I have commented out two lines which are the second part of the equation for now. If I include these two lines, it causes the values to change every time the button is pressed.


Here's the full code:




import UIKit


class ViewController: UIViewController {



@IBOutlet weak var frequencyField: UITextField!

@IBOutlet weak var waveLengthField: UITextField!

@IBOutlet weak var timePeriodField: UITextField!

var freq = Float(0);

var length = Float(0);

override func viewDidLoad() {

super.viewDidLoad()

// Do any additional setup after loading the view, typically from a nib.

}


override func didReceiveMemoryWarning() {

super.didReceiveMemoryWarning()

// Dispose of any resources that can be recreated.

}

@IBAction func Calculate(_ sender: UIButton) {

freq = Float(frequencyField.text!) ?? 1

length = Float(waveLengthField.text!) ?? 1

//print(freq)

//print(length)

let speed = Float(343.0);

var resultWave = Float(speed / freq)

waveLengthField.text! = String(resultWave)

//var resultFreq = Float (speed / length) I have commented out these two lines, but this is the second part of the equation.

//frequencyField.text! = String(resultFreq)


//frequencyField.text! = "\(resultFreq)"

// waveLengthField.text! = String(resultWave)

}

}

This should work.

Problem was a possible zero div crash, when field was empty


import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var frequencyField: UITextField!
    @IBOutlet weak var waveLengthField: UITextField!
    @IBOutlet weak var timePeriodField: UITextField!

    var freq = Float(0);
    var length = Float(0);

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }


    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func Calculate(_ sender: UIButton) {

        freq = Float(frequencyField.text!) ?? 1.0    
        length = Float(waveLengthField.text!) ?? 1.0
        print(freq)          // Just to see when they are 0
        print(length)
        let speed = Float(343.0)
        if freq > 0 {     // Avoid a zero div in some cases
             let resultWave = Float(speed / freq)
             waveLengthField.text! = String(resultWave)
       } else {
             waveLengthField.text! = "No freq defined"
      }
        if length > 0 {     // Avoid a zero div in some cases
          let resultFreq = Float (speed / length)   //   I have commented out these two lines, but this is the second part of the equation.
          frequencyField.text! = String(resultFreq)
       } else {
             frequencyField.text! = "No length defined"
      }

     // WHY would you need this ?
        //frequencyField.text! = "\(resultFreq)"
       // waveLengthField.text! = String(resultWave)

    }

}

Maybe you have not clarified your app's spec.


consisting of 3 UITextFields which allows the user to enter a number into any of the fields and recieve an output in the other two fields

What and where do you want to show when user enter two numbers into `frequencyField` and `waveLengthField` ?

Yes managed to get it working with something similar to this, thank you! This was the code in the end:


import UIKit


class ViewController: UIViewController {



@IBOutlet weak var frequencyField: UITextField!

@IBOutlet weak var waveLengthField: UITextField!

@IBOutlet weak var timePeriodField: UITextField!

var freq = Float(0);

var length = Float(0);

override func viewDidLoad() {

super.viewDidLoad()

// Do any additional setup after loading the view, typically from a nib.

}


override func didReceiveMemoryWarning() {

super.didReceiveMemoryWarning()

// Dispose of any resources that can be recreated.

}

@IBAction func Calculate(_ sender: UIButton) {

freq = Float(frequencyField.text!) ?? 0.0

length = Float(waveLengthField.text!) ?? 0.0

print(freq)

print(length)

let speed = Float(343.0)

if freq > 0 {

let resultWave = Float(speed / freq)

waveLengthField.text! = String(resultWave)

print(resultWave)

} else {

let resultWave = length

waveLengthField.text! = String(resultWave)

}

if length > 0 {

let resultFreq = Float(speed / length)

frequencyField.text! = String(resultFreq)

} else {

let freqPrint = freq

frequencyField.text! = String(freqPrint)

}


}

}

Basically, I would like to have 3 texfields labelled as follows, with a calculate button below to return values into whichever fields the user wants to know.


Frequency ...

Wavelength ...

Time Period ...

"Calculate"


- if the user enters a value in the frequency field, the wavelength = 343 / frequency and the time period = 1/frequency

- if they enter the wavelength instead, the frequency = 343 / wavelength and the time period = wavelength / 343

- if they enter the time period, the frequency = 1/time period and the wavelength = time period * 343


With the help of the comment above (Claude31), I have managed to get two of the text boxes working properly with the following code, just need to add the 3rd text box for time period...


import UIKit


class ViewController: UIViewController {



@IBOutlet weak var frequencyField: UITextField!

@IBOutlet weak var waveLengthField: UITextField!

@IBOutlet weak var timePeriodField: UITextField!

var freq = Float(0);

var length = Float(0);

override func viewDidLoad() {

super.viewDidLoad()

// Do any additional setup after loading the view, typically from a nib.

}


override func didReceiveMemoryWarning() {

super.didReceiveMemoryWarning()

// Dispose of any resources that can be recreated.

}

@IBAction func Calculate(_ sender: UIButton) {

freq = Float(frequencyField.text!) ?? 0.0

length = Float(waveLengthField.text!) ?? 0.0

print(freq) // Just to see when they are 0

print(length)

let speed = Float(343.0)

if freq > 0 { // Avoid a zero div in some cases

let resultWave = Float(speed / freq)

waveLengthField.text! = String(resultWave)

print(resultWave)

} else {

let resultWave = length

waveLengthField.text! = String(resultWave)

}

if length > 0 { // Avoid a zero div in some cases

let resultFreq = Float(speed / length)

frequencyField.text! = String(resultFreq)

} else {

let freqPrint = freq

frequencyField.text! = String(freqPrint)

}


}

}

Thanks for clarification.


- if the user enters a value in the frequency field, the wavelength = 343 / frequency and the time period = 1/frequency

- if they enter the wavelength instead, the frequency = 343 / wavelength and the time period = wavelength / 343

- if they enter the time period, the frequency = 1/time period and the wavelength = time period * 343


Then you need to distinguish which text field holds user entered value.

With your current code that is impossible.


You may need to write something like this:

class ViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet weak var frequencyField: UITextField!
  
    @IBOutlet weak var waveLengthField: UITextField!
  
    @IBOutlet weak var timePeriodField: UITextField!
  
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        frequencyField.delegate = self
        waveLengthField.delegate = self
        timePeriodField.delegate = self
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
  
    @IBAction func Calculate(_ sender: UIButton) {
      
        let speed: Float = 343.0
        if latestUserInputField === frequencyField {
            if let freq = Float(frequencyField.text!), freq > 0 {
                let resultWave = speed / freq
                waveLengthField.text = String(resultWave)
                //...
            }
        } else if latestUserInputField === waveLengthField {
            if let length = Float(waveLengthField.text!), length > 0 {
                let resultFreq = speed / length
                frequencyField.text = String(resultFreq)
                //...
            }
        } else if latestUserInputField === timePeriodField {
            //...
        }
    }
    
    var latestUserInputField: UITextField? = nil
  
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        latestUserInputField = textField
        return true
    }
  
}

Yes this was very helpful and a much cleaner way than what I was doing, thanks!