Libiph
Libiph

Reputation: 537

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

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

Upvotes: 37

Views: 62510

Answers (8)

Sergey Sergeyev
Sergey Sergeyev

Reputation: 844

Dropdown

import SwiftUI

struct DropdownView<Control, Dropdown>: View where Control: View, Dropdown: View {
    @State var show: Bool = true
    let control: () -> Control
    let dropdown: () -> Dropdown

    var body: some View {
        control() // <--(1)
            .opacity(show ? 0.7 : 1.0)
            .zIndex(1)
            .onTapGesture {
                show.toggle()
            }
            .overlay(alignment: .bottomLeading) {
                Group {
                    if show {
                        dropdown() // <--(2)
                            .transition(.opacity)
                    }
                }
                .alignmentGuide(.bottom) { $0[.top] }
            }
            .animation(.easeOut(duration: 0.2), value: show)
    }
}

// Preview Example:

struct DropdownView_Previews: PreviewProvider {
    static var previews: some View {
        VStack(alignment: .leading) {
            
            DropdownView { // <--(Use)
                Text("Dropdown")
                    .background(Color.red)
            } dropdown: {
                HStack {
                    VStack(alignment: .leading) {
                        Button("Menu item 1111") {}
                        Button("Menu item 2222") {}
                        Button("Menu item 3333") {}
                    }
                    Spacer()
                }
                //.frame(width: 140)
                .background(Color.gray)
            }
            
            Text("Lorem ipsum dolor") // example
        }
    }
}

Control limits the width of the dropdown menu:

enter image description here

Upvotes: 1

Ben216k
Ben216k

Reputation: 627

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

Menu {
    Button {
        // do something
    } label: {
        Text("Linear")
        Image(systemName: "arrow.down.right.circle")
    }
    Button {
        // do something
    } label: {
        Text("Radial")
        Image(systemName: "arrow.up.and.down.circle")
    }
} label: {
     Text("Style")
     Image(systemName: "tag.circle")
}

Upvotes: 36

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.

Upvotes: 2

atalayasa
atalayasa

Reputation: 3480

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

Upvotes: 19

vikas kumar
vikas kumar

Reputation: 11018

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.

Upvotes: 10

Joshua Jiang
Joshua Jiang

Reputation: 91

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

Upvotes: 8

Just a coder
Just a coder

Reputation: 16700

           Text("Options")
            .contextMenu {
                Button(action: {
                    // change country setting
                }) {
                    Text("Choose Country")
                }

                Button(action: {
                    // enable geolocation
                }) {
                    Text("Detect Location")
                }
            }

taken from https://www.hackingwithswift.com/quick-start/swiftui/how-to-show-a-context-menu Right click it to show the view

Upvotes: 6

user7885981
user7885981

Reputation:

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

Upvotes: 11

Related Questions