A SwiftUI Example Tutorial

Now that some of the fundamentals of SwiftUI development have been covered, this chapter will begin to put this theory into practice through the design and implementation of an example SwiftUI-based project.

The objective of this chapter is to demonstrate the use of Xcode to design a simple interactive user interface, making use of views, modifiers, state variables, and some basic animation techniques. Throughout the course of this tutorial, a variety of different techniques will be used to add and modify views. While this may appear to be inconsistent, the objective is to gain familiarity with the different options available.

Creating the Example Project

Start Xcode and select the option to create a new project. On the template selection screen, make sure Multiplatform is selected and choose the App option as shown in Figure 23-1 before proceeding to the next screen:

Figure 23-1

On the project options screen, name the project SwiftUIDemo before clicking Next to proceed to the final screen. Choose a suitable filesystem location for the project and click on the Create button.

 

You are reading a sample chapter from SwiftUI Essentials – iOS 16 Edition.

Buy the full book now in eBook (PDF, ePub, and Kindle) or Print format.

The full book contains 64 chapters and over 560 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

Reviewing the Project

Once the project has been created it will contain the SwiftUIDemoApp.swift file along with a SwiftUI View file named ContentView.swift which should have loaded into the editor and preview canvas ready for modification (if it has not loaded, simply select it in the project navigator panel). From the target device menu (Figure 23-2) select an iPhone 13 simulator:

Figure 23-2

If the preview canvas is in the paused state, click on the Resume button to build the project and display the preview:

Figure 23-3

Adding a VStack to the Layout

The view body currently consists of a single Text view of which we will make use in the completed project. A container view now needs to be added so that other views can be included in the layout. For the purposes of this example, the layout will be stacked vertically so a VStack needs to be added to the layout.

 

You are reading a sample chapter from SwiftUI Essentials – iOS 16 Edition.

Buy the full book now in eBook (PDF, ePub, and Kindle) or Print format.

The full book contains 64 chapters and over 560 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

Within the code editor, select the Text view entry, hold down the Command key on the keyboard and perform a left-click. From the resulting menu, select the Embed in VStack option:

Figure 23-4

Once the Text view has been embedded into the VStack the declaration will read as follows:

struct ContentView: View {
    var body: some View {
        VStack {
            Text("Hello, world!")
                .padding()
        }
    }
}Code language: Swift (swift)

Before modifying the view, remove the padding() modifier from the Text view:

struct ContentView: View {
    var body: some View {
        VStack {
            Text("Hello, world!")

        }
    }
}Code language: Swift (swift)

Adding a Slider View to the Stack

The next item to be added to the layout is a Slider view. Within Xcode, display the Library panel by clicking on the ‘+’ button highlighted in Figure 23-5, locate the Slider in the View list and drag it over the top of the existing Text view within the preview canvas. Make sure the notification panel (also highlighted in Figure 23-5) indicates that the view is going to be inserted into the existing stack (as opposed to being placed in a new vertical stack) before dropping the view into place.

 

You are reading a sample chapter from SwiftUI Essentials – iOS 16 Edition.

Buy the full book now in eBook (PDF, ePub, and Kindle) or Print format.

The full book contains 64 chapters and over 560 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

Figure 23-5

Once the slider has been dropped into place, the view implementation should read as follows:

struct ContentView: View {
    var body: some View {
        VStack {
            VStack {
                Text("Hello, world!")
                Slider(value: Value)
            }   
        }
    }
}Code language: Swift (swift)

Adding a State Property

The slider is going to be used to control the degree to which the Text view is to be rotated. As such, a binding needs to be established between the Slider view and a state property into which the current rotation angle will be stored. Within the code editor, declare this property and configure the Slider to use a range between 0 and 360 in increments of 0.1:

struct ContentView: View {
    
    @State private var rotation: Double = 0
    
    var body: some View {
        VStack {
            VStack {
                Text("Hello, world!")
                Slider(value: $rotation, in: 0 ... 360, step: 0.1)
            }     
        }
    }
}Code language: Swift (swift)

Note that since we are declaring a binding between the Slider view and the rotation state property it is prefixed by a ‘$’ character.

Adding Modifiers to the Text View

The next step is to add some modifiers to the Text view to change the font and to adopt the rotation value stored by the Slider view. Begin by displaying the Library panel, switch to the modifier list and drag and drop a font modifier onto the Text view entry in the code editor:

 

You are reading a sample chapter from SwiftUI Essentials – iOS 16 Edition.

Buy the full book now in eBook (PDF, ePub, and Kindle) or Print format.

The full book contains 64 chapters and over 560 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

Figure 23-6

Select the modifier line in the editor, refer to the Attributes inspector panel and change the font property from Title to Large Title as shown in Figure 23-7:

Figure 23-7

Note that the modifier added above does not change the font weight. Since modifiers may also be added to a view from within the Attributes inspector, take this opportunity to change the setting of the Weight menu from Inherited to Heavy.

On completion of these steps, the View body should read as follows:

 

You are reading a sample chapter from SwiftUI Essentials – iOS 16 Edition.

Buy the full book now in eBook (PDF, ePub, and Kindle) or Print format.

The full book contains 64 chapters and over 560 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

var body: some View {
    VStack {
        VStack {
            Text("Hello, world!")
                .font(.largeTitle)
                .fontWeight(.heavy)
                Slider(value: $rotation, in: 0 ... 360, step: 0.1)
        }
    }
}Code language: Swift (swift)

Adding Rotation and Animation

The next step is to add the rotation and animation effects to the Text view using the value stored by the Slider (animation is covered in greater detail in the “SwiftUI Animation and Transitions” chapter). This can be implemented using a modifier as follows:

.
.
Text("Hello, world!")
    .font(.largeTitle)
    .fontWeight(.heavy)
    .rotationEffect(.degrees(rotation))
.
.Code language: Swift (swift)

Note that since we are simply reading the value assigned to the rotation state property, as opposed to establishing a binding, the property name is not prefixed with the ‘$’ sign notation.

Click on the Live Preview button (indicated by the arrow in Figure 23-8), wait for the code to compile, then use the slider to rotate the Text view:

Figure 23-8

Next, add an animation modifier to the Text view to animate the rotation over 5 seconds using the Ease In Out effect:

 

You are reading a sample chapter from SwiftUI Essentials – iOS 16 Edition.

Buy the full book now in eBook (PDF, ePub, and Kindle) or Print format.

The full book contains 64 chapters and over 560 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

Text("Hello, world!")
    .font(.largeTitle)
    .fontWeight(.heavy)
    .rotationEffect(.degrees(rotation))
    .animation(.easeInOut(duration: 5), value: rotation)Code language: Swift (swift)

Use the slider once again to rotate the text and note that rotation is now smoothly animated.

Adding a TextField to the Stack

In addition to supporting text rotation, the app will also allow custom text to be entered and displayed on the Text view. This will require the addition of a TextField view to the project. To achieve this, either directly edit the View structure or use the Library panel to add a TextField so that the structure reads as follows (note also the addition of a state property in which to store the custom text string and the change to the Text view to use this property):

struct ContentView: View {
    
    @State private var rotation: Double = 0
    @State private var text: String = "Welcome to SwiftUI"
    
    var body: some View {
        VStack {
            VStack {
                Text(text)
                    .font(.largeTitle)
                    .fontWeight(.heavy)
                    .rotationEffect(.degrees(rotation))
                    .animation(.easeInOut(duration: 5))
 
                Slider(value: $rotation, in: 0 ... 360, step: 0.1)
            
                TextField("Enter text here", text: $text)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
            }
        }
    }
}Code language: Swift (swift)

When the user enters text into the TextField view, that text will be stored in the text state property and will automatically appear on the Text view via the binding. Return to the preview canvas and make sure that the changes work as expected.

Adding a Color Picker

The final view to be added to the stack before we start to tidy up the layout is a Picker view. The purpose of this view will be to allow the foreground color of the Text view to be chosen by the user from a range of color options. Begin by adding some arrays of color names and Color objects, together with a state property to hold the current array index value as follows:

import SwiftUI
 
struct ContentView: View {
    
    var colors: [Color] = [.black, .red, .green, .blue]
    var colornames = ["Black", "Red", "Green", "Blue"]
    
    @State private var colorIndex = 0
    @State private var rotation: Double = 0
    @State private var text: String = "Welcome to SwiftUI"Code language: Swift (swift)

With these variables configured, display the Library panel, locate the Picker in the Views screen and drag and drop it beneath the TextField view in either the code editor or preview canvas so that it is embedded in the existing VStack layout. Once added, the view entry will read as follows:

 

You are reading a sample chapter from SwiftUI Essentials – iOS 16 Edition.

Buy the full book now in eBook (PDF, ePub, and Kindle) or Print format.

The full book contains 64 chapters and over 560 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

Picker(selection: .constant(1, label: Text("Picker") {
    Text("1").tag(1)
    Text("2").tag(2)
}Code language: Swift (swift)

The Picker view needs to be configured to store the current selection in the colorIndex state property and to display an option for each color name in the colorNames array. To make the Picker more visually appealing, the background color for each Text view will be changed to the corresponding color in the colors array.

For the purposes of iterating through the colorNames array, the code will make use of the SwiftUI ForEach structure. At first glance, ForEach looks like just another Swift programing language control flow statement. In fact, ForEach is very different from the Swift forEach() array method outlined earlier in the book.

ForEach is itself a SwiftUI view structure designed specifically to generate multiple views by looping through a data set such as an array or range. Within the editor, modify the Picker view declaration so that it reads as follows:

Picker(selection: $colorIndex, label: Text("Color")) {
    ForEach (0 ..< colornames.count)  { color in
        Text(colornames[color])
            .foregroundColor(colors[color])
    }
}Code language: Swift (swift)

In the above implementation, ForEach is used to loop through the elements of the colornames array, generating a Text view for each color, setting the displayed text and background color on each view accordingly.

The ForEach loop in the above example is contained within a closure expression. As outlined in the chapter entitled Swift Functions, Methods, and Closures, this expression can be simplified using shorthand argument names. Using this technique, modify the Picker declaration so that it reads as follows:

 

You are reading a sample chapter from SwiftUI Essentials – iOS 16 Edition.

Buy the full book now in eBook (PDF, ePub, and Kindle) or Print format.

The full book contains 64 chapters and over 560 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

Picker(selection: $colorIndex, label: Text("Color")) {
    ForEach (0 ..< colornames.count) { color in
        Text(colornames[$0])
            .foregroundColor(colors[$0])
    }
}Code language: PHP (php)

The Picker view may be configured to display the color choices in a range of different ways. For this project, we need to select the WheelPickerStyle (.wheel) style via the pickerStyle() modifier:

Picker(selection: $colorIndex, label: Text("Color")) {
    ForEach (0 ..< colornames.count) {
        Text(colornames[$0])
            .foregroundColor(colors[$0])
    }
}
.pickerStyle(.wheel)Code language: Swift (swift)

Remaining in the code editor, locate the Text view and add a foreground color modifier to set the foreground color based on the current Picker selection value:

Text(text)
    .font(.largeTitle)
    .fontWeight(.heavy)
    .rotationEffect(.degrees(rotation))
    .animation(.easeInOut(duration: 5))
    .foregroundColor(colors[colorIndex])Code language: Swift (swift)

Test the app in the preview canvas and confirm that the Picker view appears with all of the color names using the corresponding foreground color and that color selections are reflected in the Text view.

Tidying the Layout

Up until this point the focus of this tutorial has been on the appearance and functionality of the individual views. Aside from making sure the views are stacked vertically, however, no attention has been paid to the overall appearance of the layout. At this point the layout should resemble that shown in Figure 23-9:

Figure 23-9

 

You are reading a sample chapter from SwiftUI Essentials – iOS 16 Edition.

Buy the full book now in eBook (PDF, ePub, and Kindle) or Print format.

The full book contains 64 chapters and over 560 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

The first improvement that is needed is to add some space around the Slider, TextField and Picker views so that they are not so close to the edge of the device display. To implement this, we will add some padding modifiers to the views:

Slider(value: $rotation, in: 0 ... 360, step: 0.1)
    .padding()
 
TextField("Enter text here", text: $text)
    .textFieldStyle(RoundedBorderTextFieldStyle())
    .padding()
 
Picker(selection: $colorIndex, label: Text("Color")) {
    ForEach (0 ..< colornames.count) {
        Text(colornames[$0])
            .foregroundColor(colors[$0])
    }
}
.pickerStyle(.wheel)
.padding()Code language: Swift (swift)

Next, the layout would probably look better if the Views were evenly spaced. One way to implement this is to add some Spacer views before and after the Text view:

VStack {
        Spacer()
        Text(text)
            .font(.largeTitle)
            .fontWeight(.heavy)
            .rotationEffect(.degrees(rotation))
            .animation(.easeInOut(duration: 5))
            .foregroundColor(colors[colorIndex])
        Spacer()
            Slider(value: $rotation, in: 0 ... 360, step: 0.1)
                .padding()
.
.Code language: Swift (swift)

The Spacer view provides a flexible space between views that will expand and contract based on the requirements of the layout. If a Spacer is contained in a stack it will resize along the stack axis. When used outside of a stack container, a Spacer view can resize both horizontally and vertically.

To make the separation between the Text view and the Slider more obvious, also add a Divider view to the layout:

.
.
VStack {
    Spacer()
    Text(text)
        .font(.largeTitle)
        .fontWeight(.heavy)
        .rotationEffect(.degrees(rotation))
        .animation(.easeInOut(duration: 5))
        .foregroundColor(colors[colorIndex])
    Spacer()
    Divider()
.
.Code language: Swift (swift)

The Divider view draws a line to indicate separation between two views in a stack container.

 

You are reading a sample chapter from SwiftUI Essentials – iOS 16 Edition.

Buy the full book now in eBook (PDF, ePub, and Kindle) or Print format.

The full book contains 64 chapters and over 560 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

With these changes made, the layout should now appear in the preview canvas as shown in Figure 23-10:

Figure 23-10

Summary

The goal of this chapter has been to put into practice some of the theory covered in the previous chapters through the creation of an example app project. In particular, the tutorial made use of a variety of techniques for adding views to a layout in addition to the use of modifiers and state property bindings. The chapter also introduced the Spacer and Divider views and made use of the ForEach structure to dynamically generate views from a data array.