Search code examples
swiftuser-interfaceswiftuicustomizationswiftui-button

How to make a custom DisclosureGroup (drop down) in SwiftUI?


I want to make a custom DisclosureGroup that has the same functionality as the default DisclosureGroup but allows me to change the color text or even put an image instead of a string.

Because DisclosureGroup does not allow me to remove the blue arrow indicator on the left, and it has limitations.

enter image description here

The default DisclosureGroup right now is:

struct ContentView: View {
    @State private var revealDetails = false

    var body: some View {
        DisclosureGroup("Show Terms", isExpanded: $revealDetails) {
            Text("Long terms and conditions here long terms and conditions here long terms and conditions here long terms and conditions here long terms and conditions here long terms and conditions here.")
        }
    }
}

I want to be able to do something like this:

struct ContentView: View {
    
    @State private var isExpanded = false
    
    var body: some View {
        CustomDisclosureGroup(isExpanded: $isExpanded, actionOnClick: {
            isExpanded.toggle()
            print("do something else")
        }, prompt: {
            HStack {
               Text("this is a custom row")
                  .foregroundColor(Color.white)
               Spacer()
               Image("customArrow")
                  .resizable()
                  .frame(width: 12, height: 12)
            }
            .background(Color.blue)
        }, expandedView: {
            VStack {
                Text("this is also a custom view")
                Text("another one")
            }
            .background(Color.red)
        })
    }
}


Solution

  • While DisclosureGroup doesn't give you the full flexibility to customize it, there are still a few things you can do:


    For more sophisticated customizations, if you are running on iOS 16 or later, consider adopting DisclosureGroupStyle.

    • Example:
      struct CustomDisclosureGroupStyle<Label: View>: DisclosureGroupStyle {
          let button: Label
          func makeBody(configuration: Configuration) -> some View {
              HStack {
                  configuration.label
                  Spacer()
                  button
                      .rotationEffect(.degrees(configuration.isExpanded ? 90 : 0))
              }
              .contentShape(Rectangle())
              .onTapGesture {
                  withAnimation {
                      configuration.isExpanded.toggle()
                  }
              }
              if configuration.isExpanded {
                  configuration.content
                      .padding(.leading, 30)
                      .disclosureGroupStyle(self)
              }
          }
      }
      
      struct ContentView: View {
          var body: some View {
              DisclosureGroup {
                //...
              }
              .disclosureGroupStyle(CustomDisclosureGroupStyle(button: Text("ok")))
          }
      }