Search code examples
iosswiftuifloating-action-buttonswiftui-button

How to hide Floating Menu on Click of Menu Items in SwiftUI


I Created Floating Menu in SwiftUI, but i am stuck in one issue, When press on bottom right plus button i open floating menu items and again press on plus button hide that floating menu items, but when press on menu items it will not hide that menu items.

Below is my code

MapContainerUIView.swift

struct MapContainerUIView: View {
    
    let flotingMenuArray = [
        MenuItemModel(title: "Camera", icon: "camera.fill"),
        MenuItemModel(title: "Photo", icon: "photo.on.rectangle"),
        MenuItemModel(title: "Share", icon: "square.and.arrow.up.fill")
    ]
    
    var body: some View {
        NavigationView{
            ZStack {
                FloatingMenu(dataSource: flotingMenuArray)
            }
        }
    }
    
    func floatingButtonAction(index: Int){
        print("Floating item tap", index)
    }
}

FloatingMenu.swift

struct FloatingMenu: View {
    init() {
        self.dataSource = []
    }
    
    init(dataSource: [MenuItemModel]) {
        self.dataSource = dataSource
    }
    
    @State var isMenuShow = false
    
    var dataSource: [MenuItemModel]
    
    var body: some View {
        
        ZStack(alignment: .trailing){
            if isMenuShow{
                Button(action: {
                    self.showMenu()
                }) {
                    Text("")
                        .frame(maxWidth: .infinity, maxHeight: .infinity)
                        .background(Color.black)
                        .opacity(0.3)
                }
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .edgesIgnoringSafeArea(.all)
            }
            HStack{
                Spacer()
                VStack(alignment: .trailing, content: {
                    Spacer()
                    ForEach(0..<dataSource.count) { index in
                        if isMenuShow {
                            MenuItems(menuItemData: dataSource[index], index: index)
                        }
                    }
                    Button(action: {
                        self.showMenu()
                    }) {
                        Image(systemName: "plus.circle.fill")
                            .resizable()
                            .frame(width: 60, height: 60)
                            .foregroundColor(Color.white)
                    }
                    .background(Color.black)
                    .cornerRadius(35)
                })
                .shadow(color: .gray, radius: 4, x: 1, y: 1)
                .padding(.init(top: 0, leading: 20, bottom: 20, trailing: 20))
            }
        }
    }
    
    func showMenu() {
        self.isMenuShow.toggle()
    }
}

MenuItems.swift

class MenuItemModel : Identifiable{
    var id = UUID()
    var title : String = ""
    var icon : String = ""
    
    init(title: String, icon: String) {
        self.title = title
        self.icon = icon
    }
}

struct MenuItems: View {
    var mapContainer = MapContainerUIView()
    var menuItemData: MenuItemModel
    var index : Int
    
    var body: some View {
        ZStack {
            HStack{
                Text(menuItemData.title)
                    .foregroundColor(.white)
                    .shadow(color: .gray, radius: 3, x: 1, y: 1)
                Button(action: {
                    FloatingMenu().isMenuShow = false
                    print("button tap: ", menuItemData.title)
                    self.mapContainer.floatingButtonAction(index: index)
                }) {
                    ZStack {
                        Circle()
                            .foregroundColor(Color.white)
                            .frame(width: 45, height: 45)
                            .shadow(color: .gray, radius: 3, x: 1, y: 1)
                        Image(systemName: menuItemData.icon)
                            .imageScale(.large)
                            .foregroundColor(.black)
                    }
                    .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 10))
                }
            }
        }
    }
}

I want to hide floating menu when press on menu items, please someone guide me.


Solution

  • Right now, you're trying to set isMenuShow on a new instance of FloatingMenu, which is why it isn't working.

    One solution is to pass a Binding from the original FloatingMenu instance.

    struct MenuItems: View {
        @Binding var isMenuShow : Bool  //<-- Here
        var mapContainer = MapContainerUIView()
        var menuItemData: MenuItemModel
        var index : Int
        
        var body: some View {
            ZStack {
                HStack{
                    Text(menuItemData.title)
                        .foregroundColor(.white)
                        .shadow(color: .gray, radius: 3, x: 1, y: 1)
                    Button(action: {
                        self.isMenuShow = false //<-- Here
                        print("button tap: ", menuItemData.title)
                        self.mapContainer.floatingButtonAction(index: index)
                    }) {
    

    And, in FloatingMenu:

    if isMenuShow {
      MenuItems(isMenuShow: $isMenuShow, //<-- Here
                menuItemData: dataSource[index],
                index: index)
    }
    

    Another solution would be to provide a closure as an argument to MenuItems that closes the menu:

    struct MenuItems: View {
        var closeMenu : () -> Void //<-- Here
        var mapContainer = MapContainerUIView()
        var menuItemData: MenuItemModel
        var index : Int
        
        var body: some View {
            ZStack {
                HStack{
                    Text(menuItemData.title)
                        .foregroundColor(.white)
                        .shadow(color: .gray, radius: 3, x: 1, y: 1)
                    Button(action: {
                        closeMenu()  //<-- Here
                        print("button tap: ", menuItemData.title)
                        //self.mapContainer.floatingButtonAction(index: index)
                    }) {
    
    if isMenuShow {
        MenuItems(closeMenu: { self.isMenuShow = false },
                  menuItemData: dataSource[index],
                  index: index)
    }