Is there a way to create a Dropdown-Menu/button in SwiftUI?

Viewed 39169

I want to create a dropdown-menu in Xcode 11 Beta 1. But i have not found a way to to it in iOS.

I have tried it with the .hidden function and found the PullDownButton, but don‘t know how to set it up

I have created this Code

struct SwiftUIView : View {
@State var array = true
@State var buttonTitle = "Zeige Deteils"

var body: some View {
    VStack {
        VStack {
            Button(action: {
                self.array.toggle()

            }) {
                Text(buttonTitle)
            }


            if array {
                VStack(spacing: 1.0) {

                    Button(action: {
                        self.buttonTitle = "Schmelzpunkt"
                        self.array.toggle()
                    }) {
                        Text("Schmelzpunkt")
                            .color(.white)
                            .padding(.all)
                    }
                    .background(Color.blue)
                    Button(action: {
                        self.buttonTitle = "Instrumentelle Analytik"
                        self.array.toggle()
                    }) {
                        Text("Instrumentelle Analytik")
                            .color(.white)
                            .padding(.all)
                            }.background(Color.blue)
                            Button(action: {
                                self.buttonTitle = "Aussehen"
                                self.array.toggle()
                            }) {
                                Text("Aussehen")
                                    .color(.white)
                                    .padding(.all)
                                    }.background(Color.blue)

                                }
                                .padding(.top)
                        }
                    }
                }
}

But can't find a was to animate the "poping-up" auf the hidden Buttons and want to the primary button to stay at its position

7 Answers

In SwiftUI 2.0 (iOS 14+) you can make a dropdown menu with Menu.

Menu {
    Button {
        style = 0
    } label: {
        Text("Linear")
        Image(systemName: "arrow.down.right.circle")
    }
    Button {
        style = 1
    } label: {
        Text("Radial")
        Image(systemName: "arrow.up.and.down.circle")
    }
} label: {
     Text("Style")
     Image(systemName: "tag.circle")
}

Using SwiftUI 2.0 you can also implement dropdown menu with DisclosureGroup here is ref.

GroupBox {
    DisclosureGroup("Menu 1") {
        Text("Item 1")
        Text("Item 2")
        Text("Item 3")
    }
}

You might want to take a look at the Picker.

struct ContentView : View {
    @State private var selection = 1
    var body: some View {
        VStack {
            Picker(selection: $selection, label: Text("Zeige Deteils")) {
                Text("Schmelzpunkt").tag(1)
                Text("Instrumentelle Analytik").tag(2)
            }
        }
    }
}

enter image description here

You need to use an overlay to display your dropdown. Otherwise, parents' layout will be wrong when you show and hide the dropdown.

demo

Here is a simple answer, and the complete answer could be found here

struct Dropdown: View {
    var options: [DropdownOption]
    var onSelect: ((_ key: String) -> Void)?

    var body: some View {
        VStack(alignment: .leading, spacing: 0) {
            ForEach(self.options, id: \.self) { option in
                DropdownOptionElement(val: option.val, key: option.key, onSelect: self.onSelect)
            }
        }

        .background(Color.white)
        .cornerRadius(dropdownCornerRadius)
        .overlay(
            RoundedRectangle(cornerRadius: dropdownCornerRadius)
                .stroke(Color.coreUIPrimary, lineWidth: 1)
        )
    }
}

struct DropdownButton: View {
    @State var shouldShowDropdown = false
    @Binding var displayText: String
    var options: [DropdownOption]
    var onSelect: ((_ key: String) -> Void)?

    let buttonHeight: CGFloat = 30
    var body: some View {
        Button(action: {
            self.shouldShowDropdown.toggle()
        }) {
            HStack {
                Text(displayText)
                Spacer()
                    .frame(width: 20)
                Image(systemName: self.shouldShowDropdown ? "chevron.up" : "chevron.down")
            }
        }
        .padding(.horizontal)
        .cornerRadius(dropdownCornerRadius)
        .frame(height: self.buttonHeight)
        .overlay(
            RoundedRectangle(cornerRadius: dropdownCornerRadius)
                .stroke(Color.coreUIPrimary, lineWidth: 1)
        )
        .overlay(
            VStack {
                if self.shouldShowDropdown {
                    Spacer(minLength: buttonHeight + 10)
                    Dropdown(options: self.options, onSelect: self.onSelect)
                }
            }, alignment: .topLeading
        )
        .background(
            RoundedRectangle(cornerRadius: dropdownCornerRadius).fill(Color.white)
        )
    }
}

Many good answers but what worked for me was something custom but very similar to Menu in swiftui

So start by creating your item for dropdown view.

example.

import SwiftUI


struct SampleDropDown: View {
    
  
    let action : (String?) -> Void
    
    var body: some View {
        
        
        VStack(alignment: .leading, spacing: 4){
            
            ForEach(0...3, id: \.self){ valueStore in
                
                Button(action: {
                    
                    
                    
                }) {
                    
                    HStack(alignment: .center, spacing: 8) {
                        
                        Image(systemName: "bell")
                            .resizable()
                            .frame(width: 30, height: 30, alignment: .center)
                            .clipShape(Circle())
                        
                        VStack (alignment: .leading){
                            Text("ANDROID" )
                                .font(.custom(Constants.FONT_REGULAR, size: 14))
                                .foregroundColor(Color.fromHex(Colors.TEXT_COLOR_PRIMARY))
                                .padding([.leading, .top], 4)
                            
                            Text("#jetpack")
                                .font(.custom(Constants.FONT_REGULAR, size: 12))
                                .foregroundColor(Color.fromHex(Colors.LIGHT_GREY))
                                .padding([.leading, .bottom], 2)
                            
                        }
                        
                        
                    }.foregroundColor(Color.fromHex(Colors.LIGHT_GREY))
                    
                }.frame(width: .none, height: .none, alignment: .center)
                
                
                Divider().background(Color.fromHex(Colors.DIVIDOR))
                
            }
            
        }.padding(.all, 12)
        .background(RoundedRectangle(cornerRadius: 6).foregroundColor(.white).shadow(radius: 2))
        
    }
}

struct SampleDropDown_Previews: PreviewProvider {

    static var previews: some View {
        SampleDropDown(action: {data in}).padding()
    }
}

ui so far:

enter image description here

Now just add this as Overlay where you want to show or on top of which you want to show.

something like this.

example.

 @State var showStoreDropDown: Bool = false
  //ui 
 HStack(alignment: .center, spacing: 16) {
                    
                    //here you UI goes 
                    
                }.overlay (
                    
                    VStack {
                        
                        if showTimeframeDropDown {
                            
                            Spacer(minLength: 40)
                            
                            SampleDropDown(action: { data in
                                
                            })
                            
                        }
                        
                    }, alignment: .topLeading
                    
                ).onTapGesture {
                    
                    showTimeframeDropDown.toggle()
                    
                }

result :

enter image description here

Note: This is just a sample code from my project, please change accordingly but the basic idea is to have drop-down view as an overlay on the host view.

Most of the answers are good But everything needs to write from scratch. So I used a UIKit DropDown and create a UIViewRepresentable from it.

import SwiftUI
import DropDown

struct DropDownViewRepresentable: UIViewRepresentable {

  @Binding var selectedItem: String //Send Selected item
  @Binding var isActive: Bool //Hide and Show the Dropdown

  let dropDown = DropDown()

  func makeUIView(context: Context) -> UIView {
    let view = UIView(frame: .zero)
    return view
  }

  func updateUIView(_ uiView: UIView, context: Context) {
    
    // The view to which the drop down will appear on
    dropDown.anchorView = uiView // UIView or UIBarButtonItem
    
    // The list of items to display. Can be changed dynamically
    dropDown.dataSource = ["Car", "Motorcycle", "Truck"]
    dropDown.dismissMode = .manual
    
    dropDown.selectionAction = { (index: Int, item: String) in
      print("Selected item: \(item) at index: \(index)")
        selectedItem = item
        isActive = false
    }
    
    dropDown.direction = .bottom
    
    // Top of drop down will be below the anchorView
    dropDown.bottomOffset = CGPoint(x: 0, y:(dropDown.anchorView?.plainView.bounds.height)!)
    
    dropDown.cancelAction = {
        print("Drop down dismissed")
    }

    dropDown.willShowAction = {
        print("Drop down will show")
    }
    
    if isActive{
        dropDown.show()
    }else{
        dropDown.hide()
    }
   }
 }

Note: I just created it to access the basic features.

Related