SwiftUI Fundamentals

Feb 28 2023 · Swift 5.7, macOS Venture 13.1, Xcode 14.2

Part 1: SwiftUI Views

05. State & Binding

Episode complete

Play next episode

Next
About this episode

Leave a rating/review

See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 04. Challenge: Views & Modifiers Next episode: 06. Challenge: State & Binding

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Transcript: 05. State & Binding

So far you’ve been working with views that don’t really do anything. They aren’t interactive. They don’t affect anything else and nothing affects them. Let’s try out something a little more complicated. Like, using a color picker to update the color of an image.

In the starter project for this episode, you’ll find a bit of UI already set up for you. Nothing you couldn’t put together yourself, at this point!

Let’s create an interface to change the background color for this Swift.

Open up the library. Try the keyboard shortcut: Shift-Command-L. Search for the Color Picker in the Views tab and drag one to the top of the VStack

ColorPicker(/*@START_MENU_TOKEN@*/"Title"/*@END_MENU_TOKEN@*/, selection: /*@START_MENU_TOKEN@*/.constant(.red)/*@END_MENU_TOKEN@*/)

Change the title to “Swifty Color”

ColorPicker("Swifty Color", ...

Now, take a look at this second argument. In fact, option click on “selection”. The type of selection is a binding to a Color.

Out of the box, you got a “constant.red” to fill that in. That will get your project compiling right away, and a “constant” binding can be super handy for previewing views, which you’ll see later. But it’s not going to help you change the color of our Swift bird.

You need to store the selection of this color picker somewhere, and then pass that selection on to your Image. So, step one! Add a new property to ContentView

struct ContentView: View {
  var swiftyColor: Color = .red

But this won’t be just a regular property. It needs to be a “State” property.

struct ContentView: View {
  @State var swiftyColor: Color = .red

If you try to just pass that into the ColorPicker , it’s not going to work…

selection: swiftyColor

Because you need a “binding” to a color and not a color. But this error will suggest the way to fix it, which is just to add a dollar sign.

$swiftyColor

Great. Now, you can pass just regular “swiftyColor” into the background for the image.

.background(swiftyColor)

Aha! We’re in business. And you can test this out to make sure that when you use the color picker, the swift’s background changes.

You can do this without running the app, via live previews. With the canvas in play mode, the preview should be automatically updating; if it doesn’t, you can click the refresh button to get things rendering. Click the color picker… Switch the color… And hey, that’s working too! Which is great, but, you might wonder why you needed to do all that state and binding business.

In SwiftUI, Views are functions of their state and data is a first class citizen. When the data driving a View changes, that View has to be re-rendered to reflect the change.

But, Views are structs. Value types! The Views aren’t mutating when things change, SwiftUI is returning new Views each time. So you need a way to set aside a View’s state as a mutable type in memory that, when triggered, will re-render the View. This is where the @State attribute comes in.

When you add the @State attribute to a View property, that property is automatically set aside in memory as a mutable type that has an actual binding to the View itself.

This @ symbol indicates you’re using a property wrapper. That’s another of those special Swift features that was introduced along with SwiftUI. It adds functionality to a property. In this case, @State is a property wrapper that updates your UI whenever the value of the property bound to it changes.

You’ll see a lot more property wrappers as you go through this course. They’re essential for controlling data flow in SwiftUI.

The second half of this bit of SwiftUI magic is “Bindings”. Normally, when you pass a value into an initialiser for a View, you are literally passing in a copy of the model as a dependency. Like how you passed “swiftyColor” right into that “background” modifier.

But, you saw that the ColorPicker takes a binding to a Color in its initialiser. With bindings, you’re passing in some way to connect directly to some data, rather than the data itself. That binding automatically updates the state whenever its value is set or changed.

When you mark a property with State, it gives you access to a bindable value. To pass a binding along, rather than just a value, you just need to add the dollar sign in front.

Back in our project, let’s try another State/Binding combo. First, add a state variable for Opacity

@State var swiftyOpacity: Double = 0.7

And then to use it, add a Slider, just below the ColorPicker. Pass in a binding to swiftyOpacity with a dollar sign and set the range to between 0 and 1

Slider(value: $swiftyOpacity, in: 0...1)

For the heck of it, let’s also change the accent color to match the current swiftyColor.

.accentColor(swiftyColor)

In that case, you can just pass in the data as usual. The slider won’t be changing swiftyColor at all. Now, down in the Images opacity modifier, pass in the new value.

.opacity(swiftyOpacity)

Are you getting the hang of this? I’ll let you go for a challenge in a minute, but first, let’s try something else. One of the strengths of SwiftUI is how composable it is. It’s really easy to create little views and add them together. It’s also easy to pull part of a view out into its own type!

In fact, Option-Command-Click on ColorPicker, and try selecting “Extract Subview”. Name the extracted view “SwiftyControls”. Embed the color picker in its own VStack and cut and paste the slider right below the color picker

You’ve got some errors, now, because this new SwiftyControls struct doesn’t have access to these properties anymore. To pass those along, copy and paste the State variables from above into the SwiftyControls struct.

@State var swiftyOpacity: Double = 0.7
@State var swiftyColor: Color = .red

The problem now is that you don’t actually want these values to be tied to SwiftyControls, and this @State is saying ‘This is the source of truth for this data’. Both of these values should really live in ContentView. So, how do you get the bindings we need into this struct?

Well, just like we’re passing bindings into the slider and the color picker, we can say we want to pass bindings into SwiftyControls! For that, use the @Binding property wrapper and delete the initial values.

@Binding var swiftyOpacity: Double
@Binding var swiftyColor: Color

For good measure, to reinforce where those state variables belong, mark them as private.

@State 🟢private var swiftyOp
@State 🟢private var swiftyCo

And then let the the error help you fill in the dependencies for that instance of SwiftyControls.

SwiftyControls(
  swiftyOpacity: $swiftyOpacity,
  swiftyColor: $swiftyColor
)

Give the whole thing one more try with a live preview! Now you’re ready for a challenge.