Example Project

The temperature conversion example in SwiftUI

I have rewritten the temperature conversion example as a SwiftUI app.

Here is the source code for the app's only view:

import SwiftUI

/* Hack to hide the keyboard on a tap gesture from
   https://designcode.io/swiftui-handbook-hide-keyboard */
extension View {
    func hideKeyboard() {
        let resign = #selector(UIResponder.resignFirstResponder)
        UIApplication.shared.sendAction(resign, to: nil, from: nil, for: nil)
    }
}

struct ContentView: View {
    @State var fahrenheitTemp : String = "68"
    @State var celsiusTemp : String = "20"
    var body: some View {
        VStack {
            TextField("",text:$fahrenheitTemp)
                .onChange(of: fahrenheitTemp) { ft in
                    if let t = Double(ft) {
                        let c = (t - 32.0)*5.0/9.0
                        celsiusTemp = String(format:"%.2f",c)
                    }
                }.keyboardType(.decimalPad).padding()
            Text("degrees Fahrenheit").font(.system(size:24)).foregroundColor(.orange)
            Text("is").font(.system(size:24)).foregroundColor(.orange)
            Text(celsiusTemp).padding()
            Text("degrees Celsius").font(.system(size:24)).foregroundColor(.orange)
        }.onTapGesture {
            hideKeyboard()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Working with text fields

To create a text field in SwiftUI we use the TextField() initializer. This initializer takes two parameters: some text to use as a label for the text field, and a reference to a state variable that will be connected to the text field. By providing a reference to one of our state variables to the text field we guarantee that anything the user types into the text field will get copied into that state variable for us to use. This saves us from having to write logic in an action method somewhere that reads that text from the text field.

Applying modifiers to interface elements

This example shows how we typically apply modifiers to elements in the user interface. When we were working with Interface Builder we would typically modify the properties of items by selecting them and then setting various of their properties in the inspector. In SwiftUI we have to set these properties in code instead.

Here is a typical example of how this works. The statement

Text("is").font(.system(size:24)).foregroundColor(.orange)

creates a text label that displays the text "is" and also sets the font and the color to use for the label. We do this by chaining a sequence of method calls on the label object after creating it with the appropriate initializer.

The documentation on the SwiftUI text element gives a complete listing of all of the modifiers you can apply to a text label. Also, when you are typing the code for an element in Xcode you will see a list of suggested methods as soon as you type the period after the Text initializer.

Adding event handlers

In addition to setting properties you can also attach various event handlers to elements by chaining in an appropriate "on" method. There are two examples of this in the program above.

The first example is adding an event handler to the text field that gets called whenever the user changes the contents of the text field. This is an onChange() handler. The second parameter to that method (which we implemented as a trailing closure here) is a closure that will get called whenever the text in the field changes. The closure takes one parameter, which is the new text for the field. In this example I use the closure to convert the Fahrenheit temperature the user has typed into a Celsius temperature that we store in a second state variable. Since that second state variable displays its value in a text label further down in the view, changes to that second state variable will automatically get displayed in the view.

Note that we typically will not need an onChange() handler for our text fields. The more common arrangement is to set up a text field with a reference to a state variable and then have a button somewhere else in the view whose action method will use that state variable to do some sort of calculation.

A second example of an event handler is a tap gesture recognizer I added to the VStack with the onTapGesture() method. For that I used a trailing closure that calls a method to hide the keyboard via a call to a method whose code I found on the Internet.