I have an app that i will run on iPad. I am setting up the sidebar but am stuck. I want to be able to select a page and then navigate within that selected page to other pages.
What is happening at the moment is, I am selecting between pages just fine in the sidebar and then in the main view, if I press a button that moves me away from that main view to another one, the sidebar becomes useless. I have to press "Back" to get back to the initial view that was chosen from the sidebar and then the sidebar becomes active again.
In the example app - if you go to "wx on Arrival" from the sidebar and then press the "Go settings" button in there to the "second layer" the sidebar doesn't work...
What I want to happen is: Regardless of how deep I go into a single selected sidebar view, I want to be able to select any of the sidebar options and go straight to what I am trying to go to. Thank you.
Here Is a very simple reproducible version of the problem, I apologise for the many pages:
import SwiftUI
@main
struct scrapAppApp: App {
var body: some Scene {
WindowGroup {
MenuView()
}
}
}
funcs.swift
class AppState: ObservableObject {
@Published var destination: String = ""
@Published var path = NavigationPath()
@Published var selectedItem: SidebarItem?
@Published var readyToNavigate : Bool = false
}
MenuView.swift
enum SidebarItem: String, Identifiable, CaseIterable {
case initialScreen = "Initial Screen"
case arrivalWx = "Wx On Arrival"
case legal = "Legal"
var id: String { self.rawValue }
var iconName: String {
switch self {
case .initialScreen:
return "house"
case .arrivalWx:
return "cloud.sun"
case .legal:
return "doc.text"
}
}
}
struct MenuView: View {
@StateObject private var app = AppState()
var body: some View {
NavigationSplitView {
List(SidebarItem.allCases, id: \.self, selection: $app.selectedItem) { item in
NavigationLink(value: item) {
Label(item.rawValue, systemImage: item.iconName)
}
}
.listStyle(SidebarListStyle())
.navigationTitle("Menu")
} detail: {
NavigationStack(path: $app.path) {
if let selectedItem = app.selectedItem {
switch selectedItem {
case .initialScreen:
FirstPage()
case .arrivalWx:
SecondPage(app: app)
case .legal:
FourthPage(app: app)
}
} else {
Text("Select an item")
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
}
}
}
FirstPage.swift
struct FirstPage: View {
var body: some View {
Text("INITIAL SCREEN")
}
}
SecondPage.swift
struct SecondPage: View {
@ObservedObject var app: AppState
var body: some View {
Text("WX on ARRIVAL")
Button("Go settings") {
app.readyToNavigate = true
}
.navigationDestination(isPresented: $app.readyToNavigate) {
ThirdPage()
}
}
}
ThirdPage.swift
struct ThirdPage: View {
@ObservedObject var app = AppState()
var body: some View {
Text("Second Layer")
}
}
FourthPage.swift
struct FourthPage: View {
@ObservedObject var app: AppState
var body: some View {
Button("LEGAL") {
}
}
}
Here is my test code that works for me on iPadOS 17.5.1.
struct MenuView: View {
@StateObject private var app = AppState()
var body: some View {
NavigationSplitView {
List(SidebarItem.allCases, selection: $app.selectedItem) { item in
NavigationLink(value: item) {
Label(item.rawValue, systemImage: item.iconName)
}
}
.listStyle(SidebarListStyle())
.navigationTitle("Menu")
} detail: {
NavigationStack(path: $app.path) {
if let selectedItem = app.selectedItem {
switch selectedItem {
case .initialScreen:
FirstPage()
case .arrivalWx:
SecondPage()
case .legal:
FourthPage()
}
} else {
Text("Select an item")
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
.environmentObject(app) // <--- here
}
}
}
class AppState: ObservableObject {
@Published var destination: String = ""
@Published var path = NavigationPath()
@Published var selectedItem: SidebarItem?
@Published var readyToNavigate: Bool = false
}
enum SidebarItem: String, Identifiable, CaseIterable {
case initialScreen = "Initial Screen"
case arrivalWx = "Wx On Arrival"
case legal = "Legal"
var id: String { self.rawValue }
var iconName: String {
switch self {
case .initialScreen:
return "house"
case .arrivalWx:
return "cloud.sun"
case .legal:
return "doc.text"
}
}
}
struct FirstPage: View {
@EnvironmentObject var app: AppState // not used
var body: some View {
Text("FirstPage INITIAL SCREEN")
}
}
struct SecondPage: View {
@EnvironmentObject var app: AppState
var body: some View {
Text("SecondPage WX on ARRIVAL")
Button("Go settings") {
app.readyToNavigate = true
}
.navigationDestination(isPresented: $app.readyToNavigate) {
ThirdPage()
}
}
}
struct ThirdPage: View {
@EnvironmentObject var app: AppState // not used
var body: some View {
Text("ThirdPage Second Layer")
}
}
struct FourthPage: View {
@EnvironmentObject var app: AppState // not used
var body: some View {
Button("FourthPage LEGAL") {
}
}
}
Note, I meant to say that in portrait mode, the sidebar disappears, but in landscape mode, it stays visible.