How it works
A DisclosureGroupStyleConfiguration is the bundle of properties SwiftUI hands to a custom disclosure group style when it draws each disclosure group. When you conform a type to DisclosureGroupStyle, the framework calls your makeBody(configuration:) with one of these values, giving you the group's label, its hidden content, and its current expansion state without exposing the underlying view types. Reach for it whenever the built-in disclosure appearance isn't enough and you want to design the header, the chevron, and the reveal animation yourself while letting SwiftUI keep ownership of the data.
Receive the configuration in makeBody(configuration:)
A
DisclosureGroupStylehas one requirement,makeBody(configuration:), and its parameter is aDisclosureGroupStyleConfiguration(surfaced through theConfigurationtype alias). SwiftUI invokes it once per disclosure group and expects you to returnsome View. InCardDisclosureStyle,func makeBody(configuration: Configuration) -> some Viewis where the whole custom layout is assembled.Place the header with configuration.label
The
labelproperty is a type-erased view holding whatever title the disclosure group was given. You position and decorate it but never construct it. The example dropsconfiguration.labelinto anHStackand styles it with.font(.headline), so the group's title renders inside your own header chrome.Read and flip the state with configuration.isExpanded
isExpandedis aBoolbinding that reflects whether the group is open and lets you change it. Reading it drives conditional UI; writing it toggles the group. Hereconfiguration.isExpandedchooses between"chevron.down"and"chevron.right"for the indicator, and the button's action callsconfiguration.isExpanded.toggle()insidewithAnimationto expand or collapse on tap.Reveal the body with configuration.content
The
contentproperty is the type-erased collapsible payload of the group. You decide when and how it appears. The example showsconfiguration.contentonly whenconfiguration.isExpandedis true and insets it with.padding(.leading, 8), so the hidden rows slide in beneath the header you built.Attach the style with disclosureGroupStyle(_:)
A custom style does nothing until you apply it to a
DisclosureGroupwith thedisclosureGroupStyle(_:)modifier, which propagates to disclosure groups in that view subtree. In the example.disclosureGroupStyle(CardDisclosureStyle())is what routes the"Wi-Fi Networks"group through the configuration-driven body above.
makeBody, change Image(systemName: configuration.isExpanded ? "chevron.down" : "chevron.right") to swap a fixed Image(systemName: "plus") for "minus" and watch the indicator track configuration.isExpanded as you tap the header.Example & preview
Press Run live & edit to compile it in your browser — then edit the Swift on the left and the preview re-renders live.
struct DisclosureGroupStyleConfigurationDemo: View {
struct CardDisclosureStyle: DisclosureGroupStyle {
func makeBody(configuration: Configuration) -> some View {
VStack(alignment: .leading, spacing: 8) {
Button {
withAnimation { configuration.isExpanded.toggle() }
} label: {
HStack {
configuration.label
.font(.headline)
Spacer()
Image(systemName: configuration.isExpanded ? "chevron.down" : "chevron.right")
}
}
.buttonStyle(.plain)
if configuration.isExpanded {
configuration.content
.padding(.leading, 8)
}
}
}
}
@State private var expanded = true
var body: some View {
DisclosureGroup("Wi-Fi Networks", isExpanded: $expanded) {
Text("Home")
Text("Office")
Text("Cafe Guest")
}
.disclosureGroupStyle(CardDisclosureStyle())
.padding()
}
}