I'm trying to make a fully custom list of expandable sections that have projects inside them in SwiftUI. This is how I want it to look in the end:
I think I have the SwiftUI code set up right, but I'm having trouble finding view modifiers to accomplish what I want.
Here is my code with most style modifiers removed for brevity:
List {
ForEach(sections, id: \.self) { section in
DisclosureGroup(isExpanded: $expand) {
ForEach(section.projectArray, id: \.self) { project in
//--- Projects ---
HStack{
Image("project")
Text(project.wrappedName)
Spacer()
}
.padding(EdgeInsets(top: 0, leading: 0, bottom:0, trailing: 0))
}
} label: {
//--- Sections ---
HStack{
Text(section.wrappedName)
Spacer()
//Custom Toggle Arrow
Button(action: {
//Toggle logic
}){
if expand{
Image("section-open")
}else{
Image("section-closed")
}
}
}
.padding(0)
}
}
}.listStyle(PlainListStyle())
I can't find anything to change DisclosureGroup
that adds a few default styles I don't want:
A
- A default expand/collapse arrow
B
- When expanded, the DisclosureGroup
's label
grows horizontally
C
- Default padding on the child elements
I checked the docs and don't see a way to remove these default styles. Any ideas how I can pull off this design?
This is a hacky way which won't work inside List
but it will work inside LazyVStack
and other stacks.
import SwiftUI
struct ContentView: View {
@State var isExpanded: Bool = false
var body: some View {
ScrollView {
LazyVStack {
DisclosureGroup("Disclosure", isExpanded: $isExpanded) {
Text("Hello, world!")
.padding()
}
.buttonStyle(DisclosureStyle(isExpanded: $isExpanded))
}
.padding(.horizontal)
}
}
}
struct DisclosureStyle: ButtonStyle {
@Binding var isExpanded: Bool
func makeBody(configuration: Configuration) -> some View {
HStack {
Image(systemName: isExpanded ? "chevron.down.circle" : "chevron.right.circle")
.if(configuration.isPressed) { image in
image.symbolVariant(.fill)
}
.foregroundStyle(isExpanded ? .green : .accentColor)
Spacer()
let font = isExpanded ? Font.headline.monospaced() : .headline
configuration.label
.font(font).id(font)
.overlay(alignment: .topTrailing) {
Rectangle().fill(.bar).frame(maxWidth: 30)
}
.foregroundStyle(isExpanded ? .green : .accentColor)
}
.background(.bar)
}
}
extension View {
@ViewBuilder func `if`<Content: View>(_ condition: Bool, transform: (Self) -> Content) -> some View {
if condition {
transform(self)
} else {
self
}
}
}
The result looks like this:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With