Reputation: 13135
I have a full code example below. The problem is that when I tap "Go to Detail" in the DogsView, the detail view is not visually pushed, although the path does get added to the path array for that router. If I tap the Cats tab and come back to the Dogs tab, I will then see the detail view. Tapping back (in the nav bar) from there returns me to the Dogs root view. What am I doing wrong?
(Note - the same thing happens if I go to the cats tab and tap "Push Stats". The StatsView won't appear until I tap the Dogs tab and then tap back to the Cats tab)
Another problem is that tapping "Go to Cats Tab" has no effect. I would expect the tab to switch since the TabView selection is based on $routeProvider.tabRouter.currentTab
Any other tips on setting up a routing system to navigate within a tab and to other tabs appreciated!
enum PetTab: Hashable {
case dogs
case cats
}
class PetTabRouter: ObservableObject {
@Published var currentTab: PetTab = .dogs
}
enum PetView: Hashable {
case detailView(itemId: String)
case statsView
}
class AppRouter: ObservableObject {
@Published var path: [PetView] = []
@Published var isModalPresented: Bool = false
@Published var modalItem: PetView?
var tabRouter: PetTabRouter
init(tabRouter: PetTabRouter) {
self.tabRouter = tabRouter
}
func navigate(to destination: PetView) {
path.append(destination)
}
func pop() {
if !path.isEmpty {
path.removeLast()
}
}
func present(_ item: PetView) {
modalItem = item
isModalPresented = true
}
func dismiss() {
if isModalPresented {
isModalPresented = false
modalItem = nil
}
else {
pop()
}
}
}
class RouteProvider: ObservableObject {
@Published var tabRouter = PetTabRouter()
lazy var dogsRouter: AppRouter = {
return AppRouter(tabRouter: tabRouter)
}()
lazy var catsRouter: AppRouter = {
return AppRouter(tabRouter: tabRouter)
}()
}
struct ContentView: View {
@StateObject private var routeProvider = RouteProvider()
var body: some View {
TabView(selection: $routeProvider.tabRouter.currentTab) {
DogsView()
.tabItem { Label("Dogs", systemImage: "dog") }
.tag(PetTab.dogs)
CatsView()
.tabItem { Label("Cats", systemImage: "cat") }
.tag(PetTab.cats)
}
.environmentObject(routeProvider)
}
}
struct DogsView: View {
@EnvironmentObject var routeProvider: RouteProvider
var body: some View {
NavigationStack(path: $routeProvider.dogsRouter.path) {
VStack {
Text("Dogs")
Button("Go to Detail") {
routeProvider.dogsRouter.navigate(to: .detailView(itemId: "123"))
}
Button("Go to Cats Tab") {
routeProvider.tabRouter.currentTab = .cats
}
Button("Go to Cats Tab and Push Detail View") {
routeProvider.catsRouter.navigate(to: .detailView(itemId: "456"))
}
}
.navigationDestination(for: PetView.self) { destination in
switch destination {
case .detailView(let itemId):
DetailView(itemId: itemId)
case .statsView:
StatsView()
}
}
}
}
}
struct CatsView: View {
@EnvironmentObject var routeProvider: RouteProvider
var body: some View {
NavigationStack(path: $routeProvider.catsRouter.path) {
VStack {
Text("Cats")
Button("Push Stats") {
routeProvider.catsRouter.navigate(to: .statsView)
}
}
.navigationDestination(for: PetView.self) { item in
switch item {
case .detailView(let itemId):
DetailView(itemId: itemId)
case .statsView:
StatsView()
}
}
}
}
}
struct DetailView: View {
let itemId: String
@EnvironmentObject var routeProvider: RouteProvider
var body: some View {
VStack {
Text("Detail View for item \(itemId)")
Button("Stats View Modal") {
//???
}
Button("Back to Home") {
//???
}
}
}
}
struct StatsView: View {
@EnvironmentObject var routeProvider: RouteProvider
var body: some View {
VStack {
Text("Stats")
Button("Dismiss") {
//???
}
}
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.teal)
}
}
Upvotes: 1
Views: 45