Disclosure Group in SwiftUI

DevTechie Inc
Jun 11, 2022

Photo by Joshua Hoehne on Unsplash

Disclosure group view was introduced in second iteration of SwiftUI with the release of iOS 14. DisclosureGroup is a view that shows or hides another content view, based on the state of a disclosure control, what’s also known as Accordion Control.

Disclosure group view is comprised of a label to identify the contents, and a control to show and hide the contents. Showing the contents puts the disclosure group into the “expanded” state, and hiding them makes the disclosure group “collapsed”.

Let’s understand a little better with an example.

struct DisclosureGroupExample: View {
    var body: some View {
        VStack(alignment: .leading) {
            DisclosureGroup("Mastering SwiftUI 3") // 1
            { // 2
               Text("Learn SwiftUI 3 with Examples in this comprehansive video course.")
            }
            DisclosureGroup("iOS 15 WidgetKit") {
                Text("Learn all there is in iOS 15 Widgets with this practical video course on WidgetKit.")
            }
            DisclosureGroup("MVVM Design Pattern and Protocols") {
                Text("Combine power of protocol oriented programming with MVVM design pattern in SwiftUI by building Task List app.")
            }
            
        }.padding()
    }
}
In this example 👆, we are creating three DisclosureGroups each representing a video course from DevTechie.com(check out the website if you are interested in those 😉).

  1. First parameter in DisclosureGroup is the Label
  2. Second parameter is a closure where you can define your label
Here is how our output looks like:

Dynamic DisclosureGroup in a List
Another overload for DisclosureGroup takes content and label closures, we will use them inside a List view. For this, we will first need a model so let’s create one now:

struct CourseListDG: Identifiable {
    var id = UUID()
    var name: String
    var desc: String
}
Let’s add some sample data for us to use with the List:

extension CourseListDG {
    static var sampleData: [CourseListDG] {
        [
            CourseListDG(name: "Mastering SwiftUI 3", desc: "Learn SwiftUI 3 with Examples in this comprehansive video course."),
            CourseListDG(name: "iOS 15 WidgetKit", desc: "Learn all there is in iOS 15 Widgets with this practical video course on WidgetKit."),
            CourseListDG(name: "MVVM Design Pattern and Protocols", desc: "Combine power of protocol oriented programming with MVVM design pattern in SwiftUI by building Task List app.")
        ]
    }
}
Next, we will create List view and iterate over our sample data. We will add DisclosureGroup as the content for List, as shown below:

struct DisclosureGroupExample: View {
    var body: some View {
        VStack(alignment: .leading) {
            List(CourseListDG.sampleData) { course in
                DisclosureGroup(
                    content: {
                        Text(course.desc)
                    },
                    label: {
                        Text(course.name)                            
                            .bold()
                            .foregroundColor(Color.orange)
                    }
                )
            }
        }.padding()
    }
}
Our output will look like this:

AccentColor for DisclosureGroup
Accent color for DisclosureGroup can be set with the help of .accentColor modifier.

struct DisclosureGroupExample: View {
    var body: some View {
        VStack(alignment: .leading) {
            List(CourseListDG.sampleData) { course in
                DisclosureGroup(
                    content: {
                        Text(course.desc)
                    },
                    label: {
                        Text(course.name)                            
                            .bold()
                            .foregroundColor(Color.orange)
                    }
                )
            }.accentColor(Color.orange)
        }.padding()
    }
}
Expanding DisclosureGroup Programmatically
DisclosureGroup has an overloaded initializer where you can bind a property to observe expand/collapse state. Changing state of bound property will also expand/collapse the DisclosureGroup.

For this example, we will add a button above our DisclosureGroup List, which will toggle the expanded state variable and our group will expand and collapse accordingly.

struct DisclosureGroupExample: View {
    @State private var expanded = false
    var body: some View {
        VStack(alignment: .leading) {
            Button(expanded ? "Collapse" : "Expand") {
                expanded.toggle()
            }
            List(CourseListDG.sampleData) { course in
                DisclosureGroup(
                    isExpanded: $expanded,
                    content: {
                        Text(course.desc)
                    },
                    label: {
                        Text(course.name)                            
                            .bold()
                            .foregroundColor(Color.orange)
                    }
                ).accentColor(Color.orange)
            }
            
        }.padding()
    }
}
Nested DisclosureGroup
DisclosureGroup views can be nested into other DisclosureGroup as well.

We will modify our model to add chapter string array so we can further expand our DisclosureGroup.

struct CourseListDG: Identifiable {
    var id = UUID()
    var name: String
    var desc: String
    var chapters: [String]
}
We will also update our sample data:

extension CourseListDG {
    static var sampleData: [CourseListDG] {
        [
            CourseListDG(name: "Mastering SwiftUI 3", desc: "Learn SwiftUI 3 with Examples in this comprehansive video course.", chapters: ["Intro", "Project Setup", "Model Creation"]),
            CourseListDG(name: "iOS 15 WidgetKit", desc: "Learn all there is in iOS 15 Widgets with this practical video course on WidgetKit.", chapters: ["Intro", "Project Setup", "Model Creation"]),
            CourseListDG(name: "MVVM Design Pattern and Protocols", desc: "Combine power of protocol oriented programming with MVVM design pattern in SwiftUI by building Task List app.", chapters: ["Intro", "Project Setup", "Model Creation"])
        ]
    }
}
And finally, we will add nested DisclosureGroup:

struct DisclosureGroupExample: View {
    var body: some View {
        VStack(alignment: .leading) {
            List(CourseListDG.sampleData) { course in
                DisclosureGroup(
                    content: {
                        ForEach(course.chapters, id: \.self) { chapter in 
                            DisclosureGroup(
                                content: {
                                    Text("Watch video on \(chapter)")
                                },
                                label: {
                                    Text(course.name + " - " + chapter)
                                }
                            )
                        }
                    },
                    label: {
                        Text(course.name)                            
                            .bold()
                            .foregroundColor(Color.orange)
                    }
                ).accentColor(Color.orange)
            }
            
        }.padding()
    }
}
With that we have reached the end of this article. Thank you once again for reading. Don’t forget to subscribe to our weekly newsletter at https://www.devtechie.com