I'm trying to customize a DisclosureGroup
via a custom DisclosureGroupStyle
to move the expand/collapse caret to the very left (as opposed to the standard placement on the right).
I've got the header part down but I can't get the content to render the same as it does when using the standard AutomaticDisclosureGroupStyle
.
Here is the definition of my style:
struct MyDisclosureStyle: DisclosureGroupStyle {
func makeBody(configuration: Configuration) -> some View {
VStack {
Button {
withAnimation {
configuration.isExpanded.toggle()
}
} label: {
HStack(alignment: .firstTextBaseline) {
Image(systemName: "chevron.right")
.foregroundColor(.accentColor)
.rotationEffect(.degrees(configuration.isExpanded ? 90 : 0))
.animation(
.easeInOut(duration: 0.3),
value: configuration.isExpanded
)
configuration.label
}
.contentShape(Rectangle())
}
.buttonStyle(.plain)
if configuration.isExpanded {
configuration.content
}
}
}
}
Here is a comparison of how the automatic and custom styles render:
How can I get configuration.content
to "resolve its appearance automatically based on the current context" as AutomaticDisclosureGroupStyle
does to maintain the List
styling that I'm placing the DisclosureGroup
in?
Here is the usage code:
struct ContentView: View {
@State private var isExpanded = true
var body: some View {
VStack {
List {
DisclosureGroup(isExpanded: $isExpanded) {
ForEach(1..<3) {
Text($0.formatted())
}
} label: {
Text("AutomaticDisclosureGroupStyle")
}
.disclosureGroupStyle(.automatic)
}
Divider()
List {
DisclosureGroup(isExpanded: $isExpanded) {
ForEach(1..<3) {
Text($0.formatted())
}
} label: {
Text("MyDisclosureStyle")
}
.disclosureGroupStyle(MyDisclosureStyle())
}
}
}
}
Try removing the outer VStack
from the body of MyDisclosureStyle
. This way, the body delivers a collection of separate views (in conformance with ViewBuilder
) instead of a container view with the views inside:
func makeBody(configuration: Configuration) -> some View {
// VStack {
Button {
// ...
} label: {
// ...
}
.buttonStyle(.plain)
if configuration.isExpanded {
configuration.content
}
// }
}
This expands to separate rows inside the List
. You may want to make more adaptions to the styling, but it essentially works the way you want it to:
To fix the alignment of the row separator, try applying .alignmentGuide
to the button label:
Button {
withAnimation {
configuration.isExpanded.toggle()
}
} label: {
HStack(alignment: .firstTextBaseline) {
// ...
}
.contentShape(Rectangle())
.alignmentGuide(.listRowSeparatorLeading) { dimensions in
dimensions[.leading]
}
}
.buttonStyle(.plain)