Post

Replies

Boosts

Views

Activity

Reply to Driving NavigationSplitView with something other than List?
Could we use NavigationStack in sidebar like this? It seems to work for iPhone, iPad and Mac: import SwiftUI struct Thing: Identifiable, Hashable { let id = UUID() let name: String } struct ContentView: View { let things: [Thing] = [ Thing(name: "One"), Thing(name: "Two"), Thing(name: "Three") ] @EnvironmentObject var navigation: NavigationModel var body: some View { NavigationSplitView { NavigationStack { sidebar } .navigationDestination(for: Thing.ID.self) { id in DetailView(selectedThingId: id) } } detail: { } } var sidebar: some View { ScrollView(.vertical) { LazyVStack { ForEach(things) { thing in NavigationLink("Thing: \(thing.name) \( navigation.selectedThingId == thing.id ? "selected" : "" )", value: thing.id) } SomeOtherViewHere() NavigationLink("Navigate to something else", value: things[1].id) } } } } struct DetailView: View { let selectedThingId: Thing.ID? var body: some View { if let selectedThingId { Text("There is a thing ID: \(selectedThingId)") } else { Text("There is no thing.") } } } struct SomeOtherViewHere: View { var body: some View { Text("Some other view") } }
Apr ’23
Reply to SwiftUI - Nested links within NavigationStack inside a NavigationSplitView not working
Here's a workaround, updating the NavigationPath using the selection in the DetailView; this works under iPadOS and MacOS. import SwiftUI // Sample model definitions used to trigger navigation with navigationDestination API. struct SampleModel1: Hashable, Identifiable { let id = UUID() static let samples = [SampleModel1(), SampleModel1(), SampleModel1()] } struct SampleModel2: Hashable, Identifiable { let id = UUID() static let samples = [SampleModel2(), SampleModel2(), SampleModel2()] } // The initial view loaded by the app. This will initialize the NavigationSplitView struct ContentView: View { @State var path = NavigationPath() enum NavItem: Hashable, Equatable { case first } var body: some View { NavigationSplitView { List { NavigationLink(value: NavItem.first) { Label("First", systemImage: "house") } } } detail: { SampleListView(path: $path) } } } // A list of SampleModel1 instances wrapped in a NavigationStack with multiple navigationDestinations struct SampleListView: View { // Get the selection from DetailView and append to path // via .onChange @State var selection2: SampleModel2? = nil @Binding var path: NavigationPath var body: some View { NavigationStack(path: $path) { VStack { Text("Path: \(path.count)") .padding() List(SampleModel1.samples) { model in NavigationLink("Model1: \(model.id)", value: model) } .navigationDestination(for: SampleModel2.self) { model in Text("Model 2 ID \(model.id)") .navigationTitle("navigationDestination(for: SampleModel2.self)") } .navigationDestination(for: SampleModel1.self) { model in SampleDetailView(model: model, path: $path, selection2: $selection2) .navigationTitle("navigationDestination(for: SampleModel1.self)") } .navigationTitle("First") } .onChange(of: selection2) { newValue in path.append(newValue!) } } } } // A detailed view of a single SampleModel1 instance. This includes a list // of SampleModel2 instances that we would like to be able to navigate to struct SampleDetailView: View { var model: SampleModel1 @Binding var path: NavigationPath @Binding var selection2: SampleModel2? var body: some View { NavigationStack { Text("Path: \(path.count)") .padding() List(SampleModel2.samples, selection: $selection2) { model2 in NavigationLink("Model2: \(model2.id)", value: model2) // This also works (without .onChange): // Button(model2.id.uuidString) { // path.append(model2) // } } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
Aug ’22
Reply to NavigationLink inside NavigationSplitView strange behaviours
Defining the detail of the NavigationSplitView in the following manner works as expected (persisting the selection is needed for .compact class: final class NavigationModel: ObservableObject { @Published var detailSelection: String? } @main struct NavigationLink_711115App: App { @StateObject var navigationModel = NavigationModel() var body: some Scene { WindowGroup { ContentView() .environmentObject(navigationModel) } } } struct ContentView: View { @EnvironmentObject var navigationModel: NavigationModel @State var splitViewVisibility: NavigationSplitViewVisibility = .all var body: some View { NavigationSplitView(columnVisibility: $splitViewVisibility) { List(selection: $navigationModel.detailSelection) { NavigationLink("First", value: "First") } } detail: { if let detailSelection = navigationModel.detailSelection { DetailView(string: detailSelection) } else { Text("Select the first view from the list") } } .navigationSplitViewStyle(.balanced) } } struct DetailView: View { @EnvironmentObject var navigationModel: NavigationModel var string: String var body: some View { NavigationStack { List { NavigationLink("Second", value: "Second") } .navigationDestination(for: String.self, destination: { string in Text(string) }) } .navigationTitle(string) } }
Aug ’22
Reply to Driving NavigationSplitView with something other than List?
Since List is very versatile, I'm not sure why it cannot be used here. For example, the following works fine in iOS and iPadOS (by persisting the selection): import SwiftUI class Q708440NavigationModel: ObservableObject {     @Published var selectedThingId: Thing? } struct Thing: Identifiable, Hashable {     let id: UUID = UUID()     let name: String } class ThingData: ObservableObject {     @Published var things: [Thing] = [         Thing(name: "One"),         Thing(name: "Two"),         Thing(name: "Three")     ] } struct ThingDetail: View {     let thing: Thing     @EnvironmentObject var navigationModel: Q708440NavigationModel     var body: some View {         Text("Thing: \(thing.name)")     } } struct SomeOtherViewHere: View {     var body: some View {         Text("Some other view")     } } @main struct Q708440App: App {     @StateObject var navigationModel =  Q708440NavigationModel()     @StateObject var thingData = ThingData()          var body: some Scene {         WindowGroup {             Q708440(things: $thingData.things)                 .environmentObject(navigationModel)         }     } } struct Q708440: View {     @Binding var things: [Thing]     @EnvironmentObject var navigationModel: Q708440NavigationModel     var body: some View {         NavigationSplitView {             List(selection: $navigationModel.selectedThingId) {                                Section("Data driven selection") {                     ForEach(things) { thing in                         Button("Thing: \(thing.name) \( navigationModel.selectedThingId?.id == thing.id ? "selected" : "" )") {                             navigationModel.selectedThingId = thing                         }                     }                 }                 Section("Some other view") {                     SomeOtherViewHere()                 }                 Section("Non-Interactive") {                         Text("Selection-driven, three-column NavigationSplitView sometimes fails to push when collapsed to a single column. (93673059)")                 }                 Section("Another selection") {                     Button("Navigate to thing 2") { navigationModel.selectedThingId = things[1] }                 }                                  Section("Using NavtigationLink") {                     NavigationLink("Goto to thing 1", value: things[0])                 }             }         } detail: {             ThingDetail(thing: navigationModel.selectedThingId ?? Thing(name: "Select"))         }     } }
Aug ’22
Reply to NavigationStack Being Automatically dismissed
Persisting navigation path seems to get these to work as expected: import SwiftUI class NavigationModel: ObservableObject {     @Published var presentedItems: [Int] = [] } struct SidebarEntry: Identifiable, Hashable {     let id = UUID()     var localizedName: String } let sidebarEntries = [     SidebarEntry(localizedName: "Files"),     SidebarEntry(localizedName: "Bookmarks"), ] // MARK: - main - @main struct NewNavigationTestApp: App {     @State private var sidebarSelection: SidebarEntry?     @StateObject var navigationModel =  NavigationModel()     var body: some Scene {         WindowGroup {             NavigationSplitView {                 List(sidebarEntries, selection: $sidebarSelection) { entry in                     NavigationLink("\(entry.localizedName)", value: entry)                 }             } detail: {                 ZStack { // workaround                     if let sidebarSelection {                         if sidebarSelection.localizedName == "Files" {                             TestStackView()                         } else if sidebarSelection.localizedName == "Bookmarks" {                             Text("Bookmarks View")                         } else {                             Text("Unknown View")                         }                     } else {                         Text("No selection")                     }                 }             }             .environmentObject(navigationModel)         }     } } // MARK: - FilesView - struct TestStackView: View {     @EnvironmentObject var navigationModel: NavigationModel     var body: some View {         NavigationStack(path: $navigationModel.presentedItems) {             List(0 ..< 999) { idx in                 NavigationLink("\(idx)", value: idx)             }             .navigationTitle("Numbers")             .navigationDestination(for: Int.self) { idx in                 Text("Hello \(idx)")             }         }     } }
Jul ’22