RXP
RXP

Reputation: 677

Show different views from NavigationBarItem menu in SwiftUI

I am trying to show a different view based on the option chosen in the NavigationBar menu. I am getting stuck on the best way to do this. First, based on my current approach (I think it is not right!), I get a message in Xcode debugger when I press the menu item:

SideMenu[16587:1131441] [UILog] Called -[UIContextMenuInteraction updateVisibleMenuWithBlock:] while no context menu is visible. This won't do anything.

How do I fix this?

Second, When I select an option from the menu, how do I reset the bool so that it does not get executed unless it is chosen from the menu again. Trying to reset as self.showNewView = false within the if condition gives a compiler error

Here is a full executable sample code I am trying to work with. Appreciate any help in resolving this. Thank you!

struct ContentView: View {
    @State var showNewView = false
    @State var showAddView = false
    @State var showEditView = false
    @State var showDeleteView = false
    
    var body: some View {
        NavigationView {
            GeometryReader { g in
                VStack {
                    if self.showAddView {
                        AddView()
                    }
                    if self.showNewView {
                        NewView()
                    }
                    if self.showEditView {
                        EditView()
                    }
                    if self.showDeleteView {
                        DeleteView()
                    }
                }.frame(width: g.size.width, height: g.size.height)
            }
            .navigationTitle("Title")
            .navigationBarItems(leading: {
                Menu {
                    Button(action: {showNewView.toggle()}) {
                        Label("New", systemImage: "pencil")
                    }
                    Button(action: {showEditView.toggle()}) {
                        Label("Edit", systemImage: "square.and.pencil")
                    }
                } label: {
                    Image(systemName: "ellipsis.circle")
                }
            }(), trailing: {
                Menu {
                    Button(action: {showAddView.toggle()}) {
                        Label("Add", systemImage: "plus")
                    }
                    Button(action: {showDeleteView.toggle()}) {
                        Label("Delete", systemImage: "trash")
                    }
                } label: {
                    Image(systemName: "plus")
                }
            }())
        }
        .navigationBarTitleDisplayMode(.inline)
        .navigationViewStyle(StackNavigationViewStyle())
    }
}



struct NewView: View {
    var body: some View {
        GeometryReader { g in
                Text("This is New View")
        }
        .background(Color.red)
    }
}

struct EditView: View {
    var body: some View {
        GeometryReader { g in
            Text("This is Edit View")
        }
        .background(Color.green)
    }
}

struct AddView: View {
    var body: some View {
        GeometryReader { g in
            Text("This is Add View")
        }
        .background(Color.orange)
    }
}

struct DeleteView: View {
    var body: some View {
        GeometryReader { g in
            Text("This is Delete View")
        }
        .background(Color.purple)
    }
}

Here is what I get when I select each of the menu items. I would like to be able to show only one menu item. Essentially dismiss the other one when a new menu item is selected

enter image description here

enter image description here

Upvotes: 3

Views: 1340

Answers (1)

pawello2222
pawello2222

Reputation: 54426

A possible solution is to use a dedicated enum for your current view (instead of four @State properties):

enum CurrentView {
    case new, add, edit, delete
}
@State var currentView: CurrentView?

Note that you can also extract parts of code to computed properties.


Here is a full code:

enum CurrentView {
    case new, add, edit, delete
}

struct ContentView: View {
    @State var currentView: CurrentView?

    var body: some View {
        NavigationView {
            GeometryReader { g in
                content
                    .frame(width: g.size.width, height: g.size.height)
            }
            .navigationTitle("Title")
            .navigationBarTitleDisplayMode(.inline)
            .navigationBarItems(leading: leadingBarItems, trailing: trailingBarItems)
        }
        .navigationViewStyle(StackNavigationViewStyle())
    }
    
    @ViewBuilder
    var content: some View {
        if let currentView = currentView {
            switch currentView {
            case .add:
                AddView()
            case .new:
                NewView()
            case .edit:
                EditView()
            case .delete:
                DeleteView()
            }
        }
    }
    
    var leadingBarItems: some View {
        Menu {
            Button(action: { currentView = .new }) {
                Label("New", systemImage: "pencil")
            }
            Button(action: { currentView = .edit }) {
                Label("Edit", systemImage: "square.and.pencil")
            }
        } label: {
            Image(systemName: "ellipsis.circle")
        }
    }
    
    
    var trailingBarItems: some View {
        Menu {
            Button(action: { currentView = .add }) {
                Label("Add", systemImage: "plus")
            }
            Button(action: { currentView = .delete }) {
                Label("Delete", systemImage: "trash")
            }
        } label: {
            Image(systemName: "plus")
        }
    }
}

Upvotes: 2

Related Questions