A Selectable, Expanding List in SwiftUI

Challenging in UIKit, a breeze with SwiftUI

Josh Rondestvedt
Livefront

--

Displaying a simple list of values in SwiftUI is extremely easy compared to creating a UITableView in UIKit. There’s no setting delegates, conforming to protocols, or adding a table view to the view controller.

I am going to take it one step further in this article. I am going to show you how to create an expanding list with the ability to select multiple values. There wasn’t a straightforward way to build an expanding list in UIKit. In fact, there were even libraries to accomplish this.

Instead of walking through the code step by step, I will briefly explain each piece and show the entire code at the end as well as a link to the completed project.

Introduction to the Project

Let’s assume we are going to be building a view for an expense tracker app. You want to display a list of categories a user can assign for an expense entry. For simplicity’s sake, the code provided will allow a selection of a subcategory and its parent. I will leave it up to you to find a clever way exclude a parent selection if any of its subcategories are selected 😃.

Components

Data Model

To keep this example simple, we are only going to have one type for our data model. In fact, in order for this to work, the children have to be the same type as the parent. Let me explain with some code:

To satisfy SwiftUI’s List requirements, we must also conform to Identifiable. Also, our collection of selected categories will be a Set so we must have way to uniquely identify each category.

Notice how the subcategories are of the same type as the parent. In order to use SwiftUI’s expanding list functionality, this is a requirement.

View Model

Again, to keep this example simple, we will not be fetching JSON from a network call to show a list of data. Instead, we will create a default list of a few parent categories and their children.

When an object conforms to ObservableObject, each time the categories property is mutated, an event is published through its publisher. To learn more about ObservableObject, check out Apple’s documentation. You will be able to see this in action by the end of the project when we add a new subcateogry to the list.

The List View

Finally, we get to the main event. Here’s the full code for the view. I will explain the key ingredients below.

  • We create an instance of the view model and hold onto it by marking it with @StateObject .
  • We need a collection to hold onto the selected categories UUID string value. Since we want to ensure we only have one copy of a selected category and the order is not important, we will use a Set.
  • We create a List passing in the category array and use the key-path of the subcategories property. Remember, the children must be of the same type as the parent.
  • For each cell (UIKit lingo) or row, we have an Image to display if the category is selected and a Label showing the category icon and name. The disclosure indicator arrow on the right is provided by SwiftUI for this particular type of List. The label is stretched out to the full width of the row to give more room to hit the gesture recognizer. More on that next.
  • Each row’s Label has an onTapGesture to detect when the user has selected the category. Think of this as the equivalent to UIKit’s didSelectRowAt method. When a category is selected, it will either add it to the Set or remove it if it’s already present.
  • Finally, I added a toolbar button to add a default category at a random index to demonstrate the animation when responding to additions.

Wrapping Up

As you can see, this functionality is relatively simple with a little basic knowledge on how SwiftUI works. This would have been a pain in the rear to explain in UIKit with no built in functionality from Apple.

By the way, I fully realize this project won’t win any design awards. 😀

You can get the full project here.

Happy coding!

--

--

Josh Rondestvedt
Livefront

Software Developer at Branch. Founder of the iOS app seventytwo. I build cool software for Apple devices.