Creating Expandable Lists in SwiftUI: A Comprehensive Guide
Written on
Chapter 1: Introduction to Expandable Lists
Utilizing an expandable list view in your application provides users with greater control over what they see on their screens. This clever approach significantly enhances user experience, especially when managing large amounts of data.
In this tutorial, we'll explore various methods for creating an expandable list view in SwiftUI, starting with the standard use of the List modifier, followed by built-in expandable components: OutlineGroup and DisclosureGroup.
Important: Ensure your simulator or device is running iOS 14 or later to utilize these features.
Chapter 2: Building the Data Model
Before we can implement the expandable list, we need to establish a data model. Start by defining the model with the following code:
struct SFMenu: Identifiable {
var id = UUID()
var name: String
var sfSymbol: String
var subMenuItems: [SFMenu]?
// List Items
static let iPhone = SFMenu(name: "iPhone", sfSymbol: "iphone")
static let keyboard = SFMenu(name: "Keyboard", sfSymbol: "keyboard")
static let mouse = SFMenu(name: "Magicmouse", sfSymbol: "magicmouse")
static let person = SFMenu(name: "Person", sfSymbol: "person")
static let faceSmiling = SFMenu(name: "Smiling", sfSymbol: "face.smiling")
static let cross = SFMenu(name: "Cross", sfSymbol: "cross")
static let faceMask = SFMenu(name: "Face Mask", sfSymbol: "facemask.fill")
// Grouping Items
static let devices = SFMenu(name: "Devices", sfSymbol: "display", subMenuItems: [
.iPhone, .keyboard, .mouse])
static let human = SFMenu(name: "Human", sfSymbol: "person.crop.circle", subMenuItems: [
.person, .faceSmiling])
static let health = SFMenu(name: "Health", sfSymbol: "heart", subMenuItems: [
.cross, .faceMask])
}
To ensure that each item is uniquely identifiable, the model must include a property of type UUID. This will simplify later integration into a List view. The static constants within the model represent the collections that will populate the expandable list, with comments indicating their intended positions.
Chapter 3: Creating the Expanding List
Now that we have our model set up, we can proceed to create the expandable list. Add the following property inside ContentView:
let items: [SFMenu] = [.devices, .human, .health]
This property holds an array of data that will serve as the expanding list. In the body, implement the List view as follows:
List(items, children: .subMenuItems) { row in
Image(systemName: row.sfSymbol)
Text(row.name)
}
This tutorial assumes you are already acquainted with how Lists function, so we will focus on the children parameter. This parameter enables the creation of a tree-structured data model, utilizing the subMenuItems from our previously defined model.
Run the application to see your expandable menu in action!
The menu should expand and collapse when tapped.
Chapter 4: Implementing OutlineGroup
As an alternative, you can use the OutlineGroup modifier. Using the same model, include the following code in ContentView:
struct ContentView: View {
let items: [SFMenu] = [.devices, .human, .health]
var body: some View {
List {
OutlineGroup(items, children: .subMenuItems) { row in
Image(systemName: row.sfSymbol)
Text(row.name)
}
}
}
}
The syntax remains largely the same, producing a similar outcome. The decision between OutlineGroup and List comes down to personal preference or the complexity of your code.
Chapter 5: Exploring DisclosureGroup
The DisclosureGroup is inherently included in the OutlineGroup. Utilizing it independently grants you control over the disclosure state, allowing you to programmatically manage the expansion and collapse of the view. Below is an example of how DisclosureGroup functions:
struct ContentView: View {
var body: some View {
ZStack {
DisclosureGroup("This is the title") {
Text("Content 1")
Text("Content 2")
}
.padding()
}
}
}
When you run this code, you'll see the corresponding layout, which can be expanded by clicking the view.
To set it to expand by default, create a boolean state variable initialized to true and bind it to the isExpanded parameter:
struct ContentView: View {
@State var isExpanded: Bool = true
var body: some View {
ZStack {
DisclosureGroup("This is the title", isExpanded: $isExpanded) {
Text("Content 1")
Text("Content 2")
}
.padding()
}
}
}
After running the application again, notice that it expands by default.
Chapter 6: Customizing the Appearance
Let's enhance the appearance of the DisclosureGroup. By the end of this chapter, you will have a customized expandable view.
To modify the appearance of the DisclosureGroup, follow this syntax:
DisclosureGroup(
content: {
// Customize your items here.},
label: {
// Customize the title's appearance here.}
)
Using the same data model from earlier, add a property inside ContentView:
let items: [SFMenu] = [.devices, .human, .health]
In the body, implement ForEach to avoid redundancy in your code, embedding the DisclosureGroup within it:
var body: some View {
VStack {
ForEach(items, id: .id) { name in
DisclosureGroup(
content: {
// Add content here},
label: {
// Add label customization here}
)
.padding()
.border(.black, width: 1)
.accentColor(.black)
}
}
.padding()
}
To clarify the label section, modify it as follows:
label: {
HStack {
Text(name.name)
.foregroundColor(.black)Spacer()
}
}
While the views will not expand upon tapping since they currently lack content, we can employ ForEach under the content parameter to add details:
content: {
ForEach(name.subMenuItems!, id: .id) { row in
HStack {
Image(systemName: row.sfSymbol)
Spacer()
Text(row.name)
Spacer()
}
.padding()
}
},
By doing this, you break down the name.subMenuItems, extracting both the name and sfSymbol.
Now, your ContentView code is complete, and you can run the application to interact with the views.
Congratulations on reaching this point! You can find the source code for this tutorial on GitHub.
May the code be with you,
- Arc
This video, "How to create expanding lists – SwiftUI," provides a visual demonstration of the concepts discussed in this tutorial.
The second video, "Expandable List in SwiftUI," offers further insights into creating expandable lists using SwiftUI.