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

Time:08-16

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)
        })
    }
}

CodePudding user response:

After some searching and coding I created this struct that satisfies my needs and acts very similarly to the default DisclosureGroup in SwiftUI.

Here is the usage of it:


struct ContentView: View {
    
    @State private var isExpanded = false
    @AppStorage("timesClicked") private var timesClicked = 0
    
    var body: some View {
        
        ScrollView {
            CustomDisclosureGroup(animation: .easeInOut(duration: 0.2), isExpanded: $isExpanded) {
                isExpanded.toggle()
                timesClicked  = 1
            } prompt: {
                HStack(spacing: 0) {
                    Text("How to open an account in your application?")
                    Spacer()
                    Text("?")
                        .fontWeight(.bold)
                        .rotationEffect(isExpanded ? Angle(degrees: 180) : .zero)
                }
                .padding(.horizontal, 20)
            } expandedView: {
                Text("you can open an account by choosing between gmail or ICloud when opening the application")
                    .font(.system(size: 16, weight: .semibold, design: .monospaced))
            }
        }

    }
}

you can use it where ever you want by just implementing this struct in your project:


struct CustomDisclosureGroup<Prompt: View, ExpandedView: View>: View {
    
    @Binding var isExpanded: Bool

    var actionOnClick: () -> ()
    var animation: Animation?
    
    let prompt: Prompt
    let expandedView: ExpandedView
    
    init(animation: Animation?, isExpanded: Binding<Bool>, actionOnClick: @escaping () -> (), prompt: () -> Prompt, expandedView: () -> ExpandedView) {
        self.actionOnClick = actionOnClick
        self._isExpanded = isExpanded
        self.animation = animation
        self.prompt = prompt()
        self.expandedView = expandedView()
    }
    
    var body: some View {
        VStack(spacing: 0) {
            prompt
            
            if isExpanded{
                expandedView
            }
        }
        .clipped()
        .contentShape(Rectangle())
        .onTapGesture {
            withAnimation(animation) {
                actionOnClick()
            }
        }
    }
}

CodePudding user response:

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")))
        }
    }
    
  • Related