0

I have a NavigationStack that I use to move between Views in my iOS app that has some modifications done on the toolbar (Color, colorScheme, toolbar items).

To keep the same appearance on all the different views I have to define again all the different attributes in each View I reach using NavigationLinks.

Is there a way to keep what I defined in the top NavigationView across all the different views?

Code snippet below:

First View where I define the NavigationStack

NavigationStack
        {
            VStack{
             ...
            }
            .navigationTitle("Lavorazioni")
            .navigationBarTitleDisplayMode(NavigationBarItem.TitleDisplayMode.large)
            .toolbarBackground(.blue, for: .navigationBar)
            .toolbarBackground(.visible, for: .navigationBar)
            .toolbarColorScheme(.dark, for: .navigationBar)
            .toolbar {
                Button {
                    mostraProfilo.toggle()
                } label: {
                    Label("User Profile", systemImage: "person.crop.circle")
                }
            }

Second View reached by NavigationLink in VStack

var body: some View {
        VStack{
            ...
        }
        .navigationTitle("Selezione tipologia")
        .toolbarBackground(.blue, for: .navigationBar)
        .toolbarBackground(.visible, for: .navigationBar)
        .toolbarColorScheme(.dark, for: .navigationBar)
        .toolbar {
                Button {
                    mostraProfilo.toggle()
                } label: {
                    Label("User Profile", systemImage: "person.crop.circle")
                }
            }
    }

Is there a way to avoid copypasting every time the following?

.toolbarBackground(.blue, for: .navigationBar)
        .toolbarBackground(.visible, for: .navigationBar)
        .toolbarColorScheme(.dark, for: .navigationBar)
        .toolbar {
                Button {
                    mostraProfilo.toggle()
                } label: {
                    Label("User Profile", systemImage: "person.crop.circle")
                }
            }
0

1 Answer 1

0

You can create a custom ViewModifier that holds all your common toolbar modifiers.

struct MyToolbar: ViewModifier{
    //Use shared router
    @EnvironmentObject var viewRouter: MyViewRouter
    //Add unique title for each instance
    let title: String
    func body(content: Content) -> some View {
        content
            .navigationTitle(title)
            .navigationBarTitleDisplayMode(.large)
            .toolbarBackground(.blue, for: .navigationBar)
            .toolbarBackground(.visible, for: .navigationBar)
            .toolbarColorScheme(.dark, for: .navigationBar)
            .toolbar {
                Button {
                    viewRouter.mostraProfilo.toggle()
                } label: {
                    Label("User Profile", systemImage: "person.crop.circle")
                }
            }
    }
}

You can share common navigation actions with the use of a ViewRouter that is accessible to all.

The portfolio Button is a good example.

///Router to share navigation
class MyViewRouter: ObservableObject{
    //Shared portfolio variable
    @Published var mostraProfilo: Bool = false
    //You can also add things like a `NavigationPath` to make navigation easier.
    
    enum ViewOptions: String, Hashable{
        //Create a case for each view
        case something
        case somethingDifferent
        //Set the title for a view
        var title: String{
            switch self{
            case .something:
                return "Something Title"
            case .somethingDifferent:
                return "different"
            }
        }
        //List the views for each option
        @ViewBuilder var view: some View {
            switch self{
            case .somethingDifferent:
                VStack{
                    Text("something different")
                }
                //Add individually
                .modifier(MyToolbar(title: "different"))
                //View specific modifiers over the common toolbar for overridding 
                .navigationBarTitleDisplayMode(.inline)
                .toolbarBackground(.red, for: .navigationBar)

            case .something:
                VStack{
                    Text("something")
                }
                //Add individually
                .modifier(MyToolbar(title: "something"))
            }
        }
    }
}

Your Views can then use these shared Options and modifiers.

struct SharedNavigationModifiers: View {
    @StateObject var viewRouter: MyViewRouter = .init()
    var body: some View {
        NavigationStack{
            VStack{
                NavigationLink("go somewhere", value: MyViewRouter.ViewOptions.something)
                NavigationLink("different", value: MyViewRouter.ViewOptions.somethingDifferent)
            }
            //Everone can display this portfolio using the Router
                .navigationDestination(isPresented: $viewRouter.mostraProfilo) {
                    Text("Portfolio View")
                    //Add to each View
                    .modifier(MyToolbar(title: "portfolio"))
                }
                .navigationDestination(for: MyViewRouter.ViewOptions.self) { option in
                        option.view
                    //Add to all options for a more standard look or individually (See Options)
                        //.modifier(MyToolbar(title: option.title))
                    
                }
        }
        //Inject router so all `View`s in Stack have access
        .environmentObject(viewRouter)
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.