Granite (SwiftUI)

GraniteNavigation.Passthrough | Screen

Swift
public struct NavigationPassthroughComponent<Component: GraniteComponent, Payload: GranitePayload>: View {
    
    class Screen<Component: GraniteComponent, Payload: GranitePayload> {
        ...
    }
    
    @Environment(\.graniteRouter) var router
    
    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
    @Environment(\.graniteNavigationStyle) var style
    @Environment(\.graniteNavigationDestinationStyle) var destinationStyle
    @Environment(\.graniteNavigationWindowDestinationStyle) var windowStyle
    
    fileprivate var screen: Screen<Component, Payload>
    @State var loaded: Bool = false
    
    //Simulate slide in nav stack anim
    //should be customizable via destination style
    @State var isShowing: Bool = false
    
    init(screen: Screen<Component, Payload>)
    
    var destinationStyleFinal: GraniteNavigationDestinationStyle {
        self.screen.style ?? destinationStyle
    }
    
    func dismiss()
    
    var leadingItem: some View 
    
    var navBar : some View
    
    public var body: some View {
        Group {
            #if os(iOS)
            SlideAnimationContainerView($isShowing,
                      loaded: $loaded) {
                mainView
            }
            #else
            mainView
            #endif
        }
        .onDisappear {
            self.screen.clean()
            self.loaded = false
            GraniteLog("Navigation Stack Window released", level: .debug)
        }
        .onAppear {
            self.screen.build {
                loaded = true
                GraniteLog("Navigation Stack Window loaded, isPresented: \(presentationMode.wrappedValue.isPresented)", level: .debug)
            }
        }
        .onChange(of: isShowing) { state in
            guard !state else { return }
            GraniteLog("Navigated view dismissed via sliding")
            dismiss()
        }
    }
}

This is the final wrapper that GraniteNavigation's set function creates around a pushed component. When the screen builds, a wrapper is used around the mainView to add the familiar native slide/swipe animation. Entering from the trailing end of the view, it will move inwards as a normal push. This setup will also allow further customization on the developer's end, when choosing what type of animation to use for entry and dismissal.

For macOS on the other hand, it simply appears on top. Will be thinking about bringing it to macOS, but it didn't feel very natural of an experience at the time of creation.

Swift
self.screen.clean()
self.screen.build { }

This simply allows us to remove the instances of the Views and re-build them upon appear. As they are all contained in handlers such as () -> View. Since GraniteComponent sets up listeners for events on appear, we'd like to remove and re-add them whenever they are necessary to prevent memory leaks or simply to avoid memory-usage overall. GraniteNavigation.Router shows where this screen is created and keyed into the stack.