UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

How to keep drop-down menu "in place" when pressed and cover up the view below

Forums > SwiftUI

So I've got a custom drop down menu that I've created since the stock Picker isn't very nice to look at and isn't the most customizable piece of UI. It functions how I need to but one "bug" I've noticed is that when I press the drop-down, it shoots every view above it higher onto the screen/view. And thus it has become less of a dropdown and more of a push-up menu, there "drop-down" should also cover the view/field below it.

struct DropdownMenu: View {
//    Used to show or hide drop-down menu options
    @State private var isOptionsPresented: Bool = false

//    Used to bind user selection
    @Binding var selectedOption: DropdownMenuOption?

    let placeholder: String
    let options: [DropdownMenuOption]
    var body: some View {
        Button(action: {
            self.isOptionsPresented.toggle()
        }) {
            HStack {
                Text(selectedOption == nil ? placeholder : selectedOption!.option)
                    .fontWeight(.medium)
                    .foregroundColor(selectedOption == nil ? .gray : .black)
                Spacer()
                Image(systemName: self.isOptionsPresented ? "chevron.up" : "chevron.down")
                    .fontWeight(.medium)
                    .foregroundColor(.black)
            }
        }
        .padding()
        .overlay {
            RoundedRectangle(cornerRadius: 4)
                .stroke(.gray, lineWidth: 2)
        }
        .overlay(alignment: .top) {
            VStack {
                if self.isOptionsPresented {
                    Spacer(minLength: 64)
                    DropdownMenuList(options: self.options, onSelectedAction: { option in
                        self.isOptionsPresented = false
                        self.selectedOption = option
                    })
                }
            }
        }
        .padding(.horizontal)
        .padding( .bottom, self.isOptionsPresented ? CGFloat(self.options.count * 32 ) > 160
                  ? 192
                  : CGFloat(self.options.count * 32) + 32
                  : 0
        )
    }
}

and then the dropdown menu list

struct DropdownMenuList: View {
//    the dropdown menu list options
    let options: [DropdownMenuOption]
//    an action called when user select an option
    let onSelectedAction: (_ option: DropdownMenuOption) -> Void

    var body: some View {
        ScrollView {
            LazyVStack(alignment: .leading, spacing: 02) {
                ForEach(options) { option in
                    DropdownMenuListRow(option: option, onSelectedAction: self.onSelectedAction)
                }
            }
        }
        .frame(height: CGFloat(self.options.count * 32 ) > 160 ? 160 : CGFloat(self.options.count * 32))
        .padding(.vertical, 04)
        .overlay {
            RoundedRectangle(cornerRadius: 04)
                .stroke(.gray, lineWidth: 02)
        }
    }
}

2      

Hi,

I had to improvise some things but it should work like intended,

struct ContentView: View {
    @State private var selectedOption: DropdownMenuOption? = DropdownMenuOption.option1

    var body: some View {
        VStack {
            Text("Hello World!")

            DropdownMenu(selectedOption: $selectedOption, placeholder: "Placeholder", options: [DropdownMenuOption.option1, DropdownMenuOption.option2])
                .zIndex(1)

            Text("Hello Again")
        }
    }
}
struct DropdownMenu: View {
//    Used to show or hide drop-down menu options
    @State private var isOptionsPresented: Bool = false

//    Used to bind user selection
    @Binding var selectedOption: DropdownMenuOption?

    let placeholder: String
    let options: [DropdownMenuOption]

    var body: some View {
        Button(action: {
            self.isOptionsPresented.toggle()
        }) {

              HStack {
                  Text(selectedOption == nil ? placeholder : selectedOption!.rawValue)
                      .fontWeight(.medium)
                      .foregroundColor(selectedOption == nil ? .gray : .black)
                  Spacer()
                  Image(systemName: self.isOptionsPresented ? "chevron.up" : "chevron.down")
                      .fontWeight(.medium)
                      .foregroundColor(.black)
              }
              .padding(.horizontal)
              .frame(height: 60)
              .overlay {
                  RoundedRectangle(cornerRadius: 4)
                      .stroke(.gray, lineWidth: 2)
              }
              .padding(.horizontal)

        }
        .overlay(alignment: .top) {
            VStack {
                if self.isOptionsPresented {
                    Spacer(minLength: 64)
                    DropdownMenuList(options: self.options, onSelectedAction: { option in
                        self.isOptionsPresented = false
                        self.selectedOption = option
                    })
                }
            }
            .padding(.horizontal)
            .padding( .bottom, self.isOptionsPresented ? CGFloat(self.options.count * 32 ) > 160
                      ? 192
                      : CGFloat(self.options.count * 32) + 32
                      : 0
            )
        }
    }
}
struct DropdownMenuList: View {
//    the dropdown menu list options
    let options: [DropdownMenuOption]
//    an action called when user select an option
    let onSelectedAction: (_ option: DropdownMenuOption) -> Void

    var body: some View {
        ScrollView {
            LazyVStack(alignment: .leading, spacing: 02) {
                ForEach(options, id: \.self) { option in
                    Text(option.rawValue)
                        .foregroundColor(.primary)
                }
                .padding(.horizontal)
            }
        }
        .frame(height: 60)
        .background(.white)
        .overlay {
            RoundedRectangle(cornerRadius: 04)
                .stroke(.gray, lineWidth: 02)
        }
    }
}

2      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.