Granite (SwiftUI)

GraniteNavigationView

Swift
public var view: some View {
    GraniteTabView(.init(height: tabViewHeight,
                         paddingTabs: .init(top: tabBarTopPadding, leading: 0, bottom: tabBarBottomPadding, trailing: 0),
                         paddingIcons: .init(top: topPadding, leading: 0, bottom: bottomPadding, trailing: 0),
                         landscape: Device.isExpandedLayout,
                         enableHaptic: false) {
        
        Color.background.fitToContainer()
        
    }, currentTab: 0) {
        GraniteTab(ignoreEdges: [.top]) {
            Feed()
        } icon: {
            GraniteTabIcon(name: "house")
        }
    }
    .graniteNavigation(backgroundColor: Color.background) { // <---
        Image(systemName: "chevron.backward")
            .renderingMode(.template)
            .font(.title2)
            .contentShape(Rectangle())
    }
}

GraniteNavigationViews can easily be created with a simple View extension: .graniteNavigation(). Which allows for a custom leading navigation bar icon to use, when popping views back to the parent view.

Only needs to be set once per stack you'd like to create. Later we'd see how adding this view extension to a modal and/or sheet will create a targettable stack to route components or views to from anywhere in an application.

What's Happening Here?

Swift
struct GraniteNavigationView<Content: View>: View {
    @Environment(\.graniteNavigationStyle) var style
    
    @ObservedObject var routes: GraniteNavigation
    
    let content: () -> Content
    init(@ViewBuilder content: @escaping () -> Content) {
        
        if GraniteNavigation.mainSet {
            _routes = .init(initialValue: .init(isMain: false))
        } else {
            _routes = .init(initialValue: .main)
        }
        
        self.content = content
        
        GraniteLog("Navigation initializing with \(routes.id)", level: .debug)
    }
    
    var body: some View {
        mainView
            .environment(\.graniteRouter, routes.asRouter)
    }
    var mainView: some View {
        ZStack(alignment: .top) {
            
            style.backgroundColor
                .ignoresSafeArea()
                .frame(maxWidth: .infinity,
                       maxHeight: .infinity)
            
            content()// <--- your view
                .background(style.backgroundColor)
                .onDisappear {
                    GraniteLog("releasing: \(routes.id)", level: .debug)
                    routes.releaseStack()
                }
            
            GraniteRouter()
                .environmentObject(routes)
        }
        .navBarTitle()
        .navBarHidden()
    }
}

An instance of GraniteNavigation — an ObservableObject that manages the stack — is created. A ZStack is created to mimic a NavigationController.

This will manage the observation of views added to the GraniteRouter the ObservedObject is hosting to push and pop views on top of your main View.