How to get the last two items from the stack of a NavigationPath() object

77 views Asked by At

I am facing a challenge in SwiftUI and would appreciate some guidance. In my code, I'm working with a NavigationPath() object and need to obtain the last two items from the stack. Specifically, this operation should occur within a view named SomeView, which is deep in the navigation stack.

Inside the action closure of a Button within SomeView, I want to assign the last two items of the navigationControl.path to a let constant if they exist.

I've scoured various sources for a solution, but unfortunately, I haven't found one that fits my requirements. It is crucial that the last two items are obtained from the navigationControl variable retrieved from the environment. While I am aware that these items can be obtained by injecting them into SomeView during its instantiation at the parent view, this approach is not acceptable in my case.

To provide a comprehensive overview of the project, I've attached relevant code snippets from other files as well.

//  SomeView.swift

import SwiftUI

struct SomeView: View {
    @Environment(NavigationControl.self) private var navigationControl: NavigationControl
    
    var body: some View {
        Button("Tap to get the last two items in NavigationStack") {
            // Not sure what to write here.
            // It must be something like the following options just to illustrate what is desired:
            // let lastItem = navigationControl.path.last()
            // let lastItem = navigationControl.path[navigationControl.path.count]
            // However, neither of them are defined.
            // Note that only the last item assignemnt is shown here. However, it is desired to get the last two items from path.
        }
    }
}

#Preview {
    NavigationStack {
        SomeView()
            .environment(NavigationControl())
    }
}
//  NavigationControl.swift

import SwiftUI

@Observable class NavigationControl {
    var path = NavigationPath()
}
//  ContentView.swift

import SwiftUI

struct ContentView: View {
    @Environment(NavigationControl.self) private var navigationControl: NavigationControl
    var body: some View {
        @Bindable var navigationControl = navigationControl
        NavigationStack(path: $navigationControl.path)
        {
            Button("Navigate") {
                for i in 1..<5 {
                    navigationControl.path.append(i)
                }
            }
            .navigationTitle("Root View")
            .navigationDestination(for: Int.self) { number in
                SomeView()
            }
        }
    }
}

#Preview {
    NavigationStack {
        ContentView()
            .environment(NavigationControl())
    }
}
//  NavigationPathLastElementApp.swift

import SwiftUI

@main
struct NavigationPathLastElementApp: App {
    @State var navigationControl = NavigationControl()
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(navigationControl)
        }
    }
}

Any insights or suggestions on how to achieve this would be highly appreciated. Many thanks in advance for your assistance!

2

There are 2 answers

0
Olex On

It is impossible to get even one item from NavigationPath, and you want as many as two. This is a dead end – consider alternative approaches.

1
Binshad On

NavigationPath is a useful tool for handling the order of screens or views in a navigation stack. It helps you keep track of which screen is currently being displayed and manage the navigation flow within your app.

I have made some changes to your code to display views with data passing using navigation paths. Please use this if it solves your problem.

@Observable class NavigationControl {
    var path = NavigationPath()
}

@main
struct NavigationPathLastElementApp: App {
    @State var navigationControl = NavigationControl()
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(navigationControl)
        }
    }
}

struct ContentView: View {
    @Environment(NavigationControl.self) private var navigationControl: NavigationControl
    var body: some View {
        @Bindable var navigationControl = navigationControl
        NavigationStack(path: $navigationControl.path)
        {
            SomeView()
            .navigationTitle("Root View")
            .navigationDestination(for: Int.self) { int in
                SomeView1(number: int)
            }
            .navigationDestination(for: String.self) { string in
                SomeView2(string: string)
            }
        }
    }
}

struct SomeView: View {
    @Environment(NavigationControl.self) private var navigationControl: NavigationControl
    
    var body: some View {
        Button("SomeView 1") {
            navigationControl.path.append(10)
        }
        
        Button("SomeView 2") {
            navigationControl.path.append("hello world")
        }
    }
}

struct SomeView1: View {
    @Environment(NavigationControl.self) private var navigationControl: NavigationControl
    var number: Int
    
    var body: some View {
        Button("Tap") {
            print(number)
        }
    }
}

struct SomeView2: View {
    @Environment(NavigationControl.self) private var navigationControl: NavigationControl
    var string: String
    
    var body: some View {
        Button("Tap") {
            print(string)
        }
    }
}