-
-
Save JagCesar/17fa60e25ca196f5cb171d518157ee67 to your computer and use it in GitHub Desktop.
// | |
// ContentView.swift | |
// dev | |
// | |
// Created by César Pinto Castillo on 2024-07-03. | |
// | |
import SwiftUI | |
struct ContentView: View { | |
@State private var pathManager = PathManager() | |
@State private var selectedTab: Tabs = .first | |
var body: some View { | |
TabView(selection: $selectedTab) { | |
Tab("First", systemImage: "circle", value: .first) { | |
NavigationStack(path: $pathManager.firstPath) { | |
NavigationLink(value: NavigationPath.third) { | |
Text("Open Third") | |
} | |
.navigationDestination() | |
} | |
} | |
Tab("Second", systemImage: "square", value: .second) { | |
NavigationStack(path: $pathManager.secondPath) { | |
NavigationLink(value: NavigationPath.fourth) { | |
Text("Open Fourth") | |
} | |
.navigationDestination() | |
} | |
} | |
} | |
} | |
} | |
enum Tabs { | |
case first | |
case second | |
} | |
enum NavigationPath { | |
case third | |
case fourth | |
} | |
extension View { | |
func navigationDestination() -> some View { | |
self.navigationDestination(for: NavigationPath.self) { path in | |
switch path { | |
case .fourth: | |
Text("Fourth") | |
case .third: | |
Text("Third") | |
} | |
} | |
} | |
} | |
@Observable | |
class PathManager { | |
var firstPath: [NavigationPath] = [] | |
var secondPath: [NavigationPath] = [] | |
} | |
#Preview { | |
ContentView() | |
} |
@claesjacobsson That builds, bot now we have no way of knowing which views have been pushed. With the original code we can check in the array what is the latest object and even pop views.
Sorry if I was unclear, but this is a requirement for our app. We don't want to push a view that is already on top.
Ah, I see.
In your code I think the problem is the placement of the .navigationDestination()
. Try moving it...
Tab("First", systemImage: "circle", value: .first) {
NavigationStack(path: $pathManager.firstPath) {
NavigationLink(value: NavigationPath.third) {
Text("Open Third")
.navigationDestination()
}
}
}
Tab("Second", systemImage: "square", value: .second) {
NavigationStack(path: $pathManager.secondPath) {
NavigationLink(value: NavigationPath.fourth) {
Text("Open Fourth")
.navigationDestination()
}
}
}
I can also recommend Paul Hudson's article on persisting navigations paths, if that is needed. https://www.hackingwithswift.com/books/ios-swiftui/how-to-save-navigationstack-paths-using-codable
Moving it fixes it, but it defeats the purpose of using .navigationDestination
. The idea is that we define it once per navigation stack, not once per navigation link.
Yep, seems weird.
However, in a more real-world like example where views are separated and the PathManager passed as an environment object, it seems to work...
struct ContentView: View {
private var pathManager = PathManager()
@State private var selectedTab: Tabs = .first
var body: some View {
TabView(selection: $selectedTab) {
Tab("First", systemImage: "circle", value: .first) {
FirstScreen()
}
Tab("Second", systemImage: "square", value: .second) {
SecondScreen()
}
}
.environment(pathManager)
}
}
enum Tabs {
case first
case second
}
enum AppScreen {
case third
case fourth
}
extension View {
func navigationDestination() -> some View {
self.navigationDestination(for: AppScreen.self) { path in
switch path {
case .fourth:
Text("Fourth")
case .third:
Text("Third")
}
}
}
}
@Observable
class PathManager {
var firstPath: [AppScreen] = []
var secondPath: [AppScreen] = []
}
#Preview {
ContentView()
}
struct FirstScreen: View {
@Environment(PathManager.self) private var pathManager
var body: some View {
@Bindable var pathManager = pathManager
NavigationStack(path: $pathManager.firstPath) {
NavigationLink(value: AppScreen.third) {
Text("Open Third")
}
.navigationDestination()
}
}
}
struct SecondScreen: View {
@Environment(PathManager.self) private var pathManager
var body: some View {
@Bindable var pathManager = pathManager
NavigationStack(path: $pathManager.secondPath) {
NavigationLink(value: AppScreen.fourth) {
Text("Open Fourth")
}
.navigationDestination()
}
}
}
Moving it fixes it, but it defeats the purpose of using
.navigationDestination
. The idea is that we define it once per navigation stack, not once per navigation link.
I believe it's designed to be defined multiple times throughout the stack's hierarchy where different types describe the data for each screen you want to show. i.e. its supposed to be decentralised, not centralised in one destination with enum and switch which many seem to be doing for some reason.
The way I see it....
With those changes, your code would look like this: