As we’ve been working through this series introducing SwiftUI, we’ve so far made an app that allows us to view our team from a list and dive into specific members’ detailed profiles.
However, all our users can do with the app is view dynamic data in a very static UI. The only interaction we’ve provided is the navigation into a team member’s detailed profile.
Our UI could become much more interesting if we added some interactivity to enhance its user experience (UX). To do so, we’ll leverage some interactive views as well as use bindings to make our app reactive to changes and inputs.
Table of contents:
Combine, Reactive, and SwiftUI
Apple announced SwiftUI at the same time that they announced Combine, a new native Reactive framework. For anyone who’s used ReactiveCocoa, this is seen as direct competition, but is also welcomed given that it’s a native approach.
To briefly summarize the benefits of Combine and Reactive programming…It allows code to reference, subscribe, and/or bind with bindable data streams. Then, when those streams are edited, it informs the stream’s listeners so they can REACT to the changes.
Show/Hide More Information
We’ll start with a relatively simple feature: hiding and displaying profile details. It’s not the most earth-shattering feature, but it will teach us a couple of basic concepts.
To implement our feature, we’ll need three things:
- A conditional statement controlling the UI we want to change
- A trigger that will affect our conditional
- Animations and modifiers that will aide our transitional presentation
State: The Conditional of Choice
In SwiftUI, we’re introduced to the State property wrapper. This communicates that a variable is bindable by our UI. Whenever a view references or makes a change to the State, it informs other areas that reference it so they can adjust to the current State our app has transitioned into.
In ProfileInformation, below var Profile, let’s add the following:
@State var showMore: Bool = false
Now that we have our conditional variable, let’s wrap the details we want to hide/show in an if statement so that they only show when showMore is true:
If we run our app in preview or on the simulator, our Texts won’t be showing now because showMore is set to false. That’s great…but how do we switch it to true or false? This is where we’ll add our interactive control.
Button: A Simple Trigger Control
A basic interactive control in SwiftUI (and really any UI framework) is Button. Here, we can set the Button action to simply (and literally) toggle the value of showMore.
What’s also great about Button is that we can set a view to it that will become clickable. For now, we’ll just set it to a Text, but we’ll change the String it shows based on showMore’s state. Add the following below our profile.Name, but before our new if statement:
Now if we run our app, we’ll see “Show More” in blue text. When we tap it, we’ll not only see it switch to “Show Less”, but we’ll also now see our profile details. Tapping again would, of course, return our app to the previous State. We now have interaction in our app!
Now we just need to make it look good…
Animation: Cause We Live in the 21st Century
Let’s face it, having things just appear/disappear is so last century. We can very easily add animation to our app with one simple change:
If we wrap our toggle statement with withAnimation, we inform SwiftUI that any resultant transitions should be done with an automated animation. And, in this case, we specify a linear animation with a duration of 0.3 seconds. Now when we run our app, we’ll see our profile details come in and out of view with a little more pizzaz.
But we don’t need to stop there! Why don’t we make our button look good, too? Replace the Text in our button with this Image:
This Image uses a SF Symbol, which is part of a new library of system symbols now provided by Apple. We then add a modifier that takes in another ternary, except this time we either want the arrow to be rotated 0 degrees when we’re not showing more details (pointing down), or rotated -180 degrees (-/+ works, more preferences in direction) when we are showing more details (pointing up).
Just adding a simple bit of interactivity not only makes our app less static, but now allows us to start considering what else we can add and change!
Preview: Toggling Superstars
Another cool (yet very judgmental) feature we can add is toggling who on the team is a superstar or not (told you it was super judgey). Let’s add another interactive control (an actual Toggle this time) as we review the three steps to adding this interaction.
First, let’s create our State. In ProfilePage, add the following:
@State var superStar: Bool = false
To complete step 1, we use our state variable in a conditional. Why don’t we have our gray circular overlay on the profile picture turn yellow (faux gold) when set to true? Where we specify Color.gray, replace it with this:
self.superStar ? Color.yellow : Color.gray
Next, we add our control. On the bottom of ProfilePage , let’s add our Toggle and set it to bind to superStar:
Lastly, we should evaluate if there’s anything we want to modify or add to present this more effectively, but since it’s a simple color change, nothing else is needed.
If we run our app, we’ll now see that toggling the Toggle indeed changes the overlaid circle.
However, this feature in particular feels incomplete, doesn’t it? When we navigate back to our list of team members, we realize that there’s no indication that we’ve marked this member as a superstar. Worse, when we navigate back into the profile page, it doesn’t save the status at all!
This can be resolved in the next and final part of our series, where we’ll introduce ObservableObjects and see how we can make custom classes that are bindable throughout our UI.
Tip For the Road
Earlier, we had specified a linear animation type. However, there are other kinds of animations built-in to SwiftUI. An easy way to explore them is by removing .linear, typing just a single ., and seeing Xcode present a number of compatible options that you can use (e.g. easeIn, easeOut, spring). This autocomplete feature can be used elsewhere in code, but is especially helpful with building out your UI.