The SwiftUI cookbook for navigation

RSS for tag

Discuss the WWDC22 Session The SwiftUI cookbook for navigation

Posts under wwdc2022-10054 tag

45 Posts

Post

Replies

Boosts

Views

Activity

NavigationStack Being Automatically dismissed
Hello, On the recent iOS / iPadOS 16, SwiftUI 4, and Xcode Version 14.0 beta (14A5228q), my app contains a NavigationStack within the content view; and the stack contains a ForEach that iterates over a list of fruits / items. The form loop outputs a navigation link, the label being a view, and the destination being another NavigationStack. And whenever I go to click on that link, the app shoots me back to the root view instantly. This seems to happen whenever I have any kind of content in the StackNavigation that's within the NavigationLink. I will post the code snippets below, along with a GIF showing the bug in action. ContentView.Swift: struct ContentView: View {     @State private var isShowingSettings: Bool = false     var fruits: [Fruit] = fruitsData     var body: some View {         NavigationStack {             List(fruits.shuffled()) { fruit in                 NavigationLink(destination: FruitDetailView(fruit: fruit)) {                     FruitRowView(fruit: fruit) .padding(.vertical, 4)                 }             }             .navigationBarTitle("Fruits")             .toolbar {                 ToolbarItem(placement: .navigationBarTrailing) {                     Button(action: {                         isShowingSettings = true                     }) {                         Image(systemName: "slider.horizontal.3")                     }                     .sheet(isPresented: $isShowingSettings) {                         SettingsView()                     }                 }             }         }     } } FruitDetailView.Swift: // Code is shortened, but has the same bug as the actual code struct FruitDetailView: View {     var fruit: Fruit     var body: some View {         NavigationStack {             ScrollView(.vertical, showsIndicators: false) { Text(fruit.name)             }             .edgesIgnoringSafeArea(.top)         }     } Preview:
3
0
3k
Jul ’22
Swift: How to change DetailView depending on SideBar Selection State with SwiftUI 4?
Context I have a problem with the new SwiftUI SideBar / NavigationLink concept. I have created a simple 2 Column NavigationSplitView with a SideBar and a DetailView. Once the App is launched, the preselected Timeline RootView appears as the DetailView. However, when I select a different SideBar Item, the DetailView does not change accordingly. Code struct SideBar: View { @State private var navigationItem: NavigationItem? = .timeline var body: some View { NavigationSplitView { List(NavigationItem.allCases, id: \.self, selection: $navigationItem) { navigationItem in NavigationLink(value: navigationItem) { Label(navigationItem.name, systemImage: navigationItem.symbol) } } } detail: { if let safeNavigationItem = navigationItem { safeNavigationItem.rootView } else { Text(String(localized: "select.an.option", defaultValue: "Select an Option")) } } } } Question Do you have any idea how I can solve this issue? Thanks a lot for your support in advance. Note: Just ran it on macOS, it is working there. However, still not working on iPadOS.
1
0
1.8k
Jul ’22
NavigationStack state restoration inconsistent
I'm trying to manage state restoration in our application. I failed to get NavigationView/NavigationLink working in pre-iOS 16 SwiftUI. I'm now trying to get it working with NavigationStack/NavigationLink/navigationDestination with iOS 16. I've built a sample application that demonstrates the issue: import SwiftUI @main struct PushMultipleViews7App: App {     var body: some Scene {         WindowGroup {             ContentView()         }     } } enum Route: Hashable, Codable {     case view1     case view2     case view3     case view4 } struct ContentView: View {     @State     private var navigationPath: [Route]     @Environment(\.scenePhase)     private var scenePhase     init() {         guard let data = UserDefaults                   .standard                   .data(                       forKey: "NavigationPath"                   ),               let navigationPath = try? JSONDecoder()                   .decode(                       [Route].self,                       from: data                   )         else {             self._navigationPath = State(                 initialValue: []             )             print("Failed to read NavigationPath")             return         }         print("Read:",navigationPath)         self._navigationPath = State(             initialValue: navigationPath         )     }     var body: some View {         VStack {             NavigationStack(                 path: $navigationPath             )             {                 View1()                     .navigationDestination(                         for: Route.self                     )                 { route in                     switch route {                     case .view1:                         View1()                     case .view2:                         View2()                     case .view3:                         View3()                     case .view4:                         View4()                     }                 }             }         }             .onChange(                 of: scenePhase             )             { scenePhase in                 guard case .inactive = scenePhase                 else {                     return                 }                 guard let data = try? JSONEncoder()                           .encode(                               navigationPath                           )                 else {                     print("Failed to write NavigationPath")                     return                 }                 print("Writing:",navigationPath)                 UserDefaults                     .standard                     .setValue(                         data,                         forKey: "NavigationPath"                     )             }     } } struct View1: View {     var body: some View {         VStack {             Text("Hello View1!")             NavigationLink(                 value: Route.view2,                 label: {                     Text("Navigate to View2")                 }             )         }             .navigationTitle("View 1")     } } struct View2: View {     var body: some View {         VStack {             Text("Hello View2!")             NavigationLink(                 value: Route.view3,                 label: {                     Text("Navigate to View3")                 }             )         }             .navigationTitle("View 2")     } } struct View3: View {     var body: some View {         VStack {             Text("Hello View3!")             NavigationLink(                 value: Route.view4,                 label: {                     Text("Navigate to View4")                 }             )         }             .navigationTitle("View 3")     } } struct View4: View {     var body: some View {         Text("Hello, View4!")     } }``` To replicate the issue: from Xcode 14, run the app above in your favorite iOS 16 phone simulator. Tap on the NavigationLinks to navigate to View 4. Navigate to the Home Screen (put the app into the background). Wait a few seconds for UserDefaults to catch up. "Swipe up" to kill the app. Relaunch the app. View4 should be displaying...which is the expected behavior. Tap the "View 3" button (the "back" button) to display View3. As before, navigate to the Home Screen, wait a few seconds then "swipe up" to kill the app. Relaunch the app. Instead of displaying "View3", the app should be displaying View1. Evidence can be seen in the console when writing the navigation path to UserDefaults - it wrote "[]". That's just one variation among lots of strange behavior I've seen trying to restore a NavigationStack's navigation path. I've filed: FB10398702 I've tried several variations of this application: using @AppStorage and @SceneStorage instead of UserDefaults. I've also tried the sample code accompanying NavigationPath in Apple's documentation (though also based on UserDefaults). Am I doing something incorrectly or is this a bug? Thanks, --David
1
0
1.3k
Jun ’22
Reset the detail view on NavigationSplitView 3-column when a new Sidebar item is selected, but no content item
Is there a way to clear/reset the detail view when the user selects a new item in the sidebar but hasn't yet selected an item from the content panel? Playing around with the WWDC22 demo project "Navigation Cookbook", I noticed that if you select a Category, then a Recipe, the recipe details display as expected. If you then select a new category, though, the original recipe remains in the detail panel until you select a new recipe. I'd like the detail panel to reset to its original state of "Pick a Recipe".
1
0
1.2k
Jun ’22
Correct way to hide sidebar after selection.
In a triple-column split view, it's common to hide the primary view when the user selects an item from a list. In some cases, the supplemental view is also hidden when an item is selected from that list, thus leaving only the detail view visible. What is the correct way to hide the primary view on selection and then to optionally hide the supplemental view on selection using the new NavigationStack? (Notes is a good example of the sidebars hiding after the user selects a folder in the sidebar.)
1
2
1.5k
Jun ’22
NavigationStack Being Automatically dismissed
Hello, On the recent iOS / iPadOS 16, SwiftUI 4, and Xcode Version 14.0 beta (14A5228q), my app contains a NavigationStack within the content view; and the stack contains a ForEach that iterates over a list of fruits / items. The form loop outputs a navigation link, the label being a view, and the destination being another NavigationStack. And whenever I go to click on that link, the app shoots me back to the root view instantly. This seems to happen whenever I have any kind of content in the StackNavigation that's within the NavigationLink. I will post the code snippets below, along with a GIF showing the bug in action. ContentView.Swift: struct ContentView: View {     @State private var isShowingSettings: Bool = false     var fruits: [Fruit] = fruitsData     var body: some View {         NavigationStack {             List(fruits.shuffled()) { fruit in                 NavigationLink(destination: FruitDetailView(fruit: fruit)) {                     FruitRowView(fruit: fruit) .padding(.vertical, 4)                 }             }             .navigationBarTitle("Fruits")             .toolbar {                 ToolbarItem(placement: .navigationBarTrailing) {                     Button(action: {                         isShowingSettings = true                     }) {                         Image(systemName: "slider.horizontal.3")                     }                     .sheet(isPresented: $isShowingSettings) {                         SettingsView()                     }                 }             }         }     } } FruitDetailView.Swift: // Code is shortened, but has the same bug as the actual code struct FruitDetailView: View {     var fruit: Fruit     var body: some View {         NavigationStack {             ScrollView(.vertical, showsIndicators: false) { Text(fruit.name)             }             .edgesIgnoringSafeArea(.top)         }     } Preview:
Replies
3
Boosts
0
Views
3k
Activity
Jul ’22
Swift: How to change DetailView depending on SideBar Selection State with SwiftUI 4?
Context I have a problem with the new SwiftUI SideBar / NavigationLink concept. I have created a simple 2 Column NavigationSplitView with a SideBar and a DetailView. Once the App is launched, the preselected Timeline RootView appears as the DetailView. However, when I select a different SideBar Item, the DetailView does not change accordingly. Code struct SideBar: View { @State private var navigationItem: NavigationItem? = .timeline var body: some View { NavigationSplitView { List(NavigationItem.allCases, id: \.self, selection: $navigationItem) { navigationItem in NavigationLink(value: navigationItem) { Label(navigationItem.name, systemImage: navigationItem.symbol) } } } detail: { if let safeNavigationItem = navigationItem { safeNavigationItem.rootView } else { Text(String(localized: "select.an.option", defaultValue: "Select an Option")) } } } } Question Do you have any idea how I can solve this issue? Thanks a lot for your support in advance. Note: Just ran it on macOS, it is working there. However, still not working on iPadOS.
Replies
1
Boosts
0
Views
1.8k
Activity
Jul ’22
NavigationStack state restoration inconsistent
I'm trying to manage state restoration in our application. I failed to get NavigationView/NavigationLink working in pre-iOS 16 SwiftUI. I'm now trying to get it working with NavigationStack/NavigationLink/navigationDestination with iOS 16. I've built a sample application that demonstrates the issue: import SwiftUI @main struct PushMultipleViews7App: App {     var body: some Scene {         WindowGroup {             ContentView()         }     } } enum Route: Hashable, Codable {     case view1     case view2     case view3     case view4 } struct ContentView: View {     @State     private var navigationPath: [Route]     @Environment(\.scenePhase)     private var scenePhase     init() {         guard let data = UserDefaults                   .standard                   .data(                       forKey: "NavigationPath"                   ),               let navigationPath = try? JSONDecoder()                   .decode(                       [Route].self,                       from: data                   )         else {             self._navigationPath = State(                 initialValue: []             )             print("Failed to read NavigationPath")             return         }         print("Read:",navigationPath)         self._navigationPath = State(             initialValue: navigationPath         )     }     var body: some View {         VStack {             NavigationStack(                 path: $navigationPath             )             {                 View1()                     .navigationDestination(                         for: Route.self                     )                 { route in                     switch route {                     case .view1:                         View1()                     case .view2:                         View2()                     case .view3:                         View3()                     case .view4:                         View4()                     }                 }             }         }             .onChange(                 of: scenePhase             )             { scenePhase in                 guard case .inactive = scenePhase                 else {                     return                 }                 guard let data = try? JSONEncoder()                           .encode(                               navigationPath                           )                 else {                     print("Failed to write NavigationPath")                     return                 }                 print("Writing:",navigationPath)                 UserDefaults                     .standard                     .setValue(                         data,                         forKey: "NavigationPath"                     )             }     } } struct View1: View {     var body: some View {         VStack {             Text("Hello View1!")             NavigationLink(                 value: Route.view2,                 label: {                     Text("Navigate to View2")                 }             )         }             .navigationTitle("View 1")     } } struct View2: View {     var body: some View {         VStack {             Text("Hello View2!")             NavigationLink(                 value: Route.view3,                 label: {                     Text("Navigate to View3")                 }             )         }             .navigationTitle("View 2")     } } struct View3: View {     var body: some View {         VStack {             Text("Hello View3!")             NavigationLink(                 value: Route.view4,                 label: {                     Text("Navigate to View4")                 }             )         }             .navigationTitle("View 3")     } } struct View4: View {     var body: some View {         Text("Hello, View4!")     } }``` To replicate the issue: from Xcode 14, run the app above in your favorite iOS 16 phone simulator. Tap on the NavigationLinks to navigate to View 4. Navigate to the Home Screen (put the app into the background). Wait a few seconds for UserDefaults to catch up. "Swipe up" to kill the app. Relaunch the app. View4 should be displaying...which is the expected behavior. Tap the "View 3" button (the "back" button) to display View3. As before, navigate to the Home Screen, wait a few seconds then "swipe up" to kill the app. Relaunch the app. Instead of displaying "View3", the app should be displaying View1. Evidence can be seen in the console when writing the navigation path to UserDefaults - it wrote "[]". That's just one variation among lots of strange behavior I've seen trying to restore a NavigationStack's navigation path. I've filed: FB10398702 I've tried several variations of this application: using @AppStorage and @SceneStorage instead of UserDefaults. I've also tried the sample code accompanying NavigationPath in Apple's documentation (though also based on UserDefaults). Am I doing something incorrectly or is this a bug? Thanks, --David
Replies
1
Boosts
0
Views
1.3k
Activity
Jun ’22
Reset the detail view on NavigationSplitView 3-column when a new Sidebar item is selected, but no content item
Is there a way to clear/reset the detail view when the user selects a new item in the sidebar but hasn't yet selected an item from the content panel? Playing around with the WWDC22 demo project "Navigation Cookbook", I noticed that if you select a Category, then a Recipe, the recipe details display as expected. If you then select a new category, though, the original recipe remains in the detail panel until you select a new recipe. I'd like the detail panel to reset to its original state of "Pick a Recipe".
Replies
1
Boosts
0
Views
1.2k
Activity
Jun ’22
Correct way to hide sidebar after selection.
In a triple-column split view, it's common to hide the primary view when the user selects an item from a list. In some cases, the supplemental view is also hidden when an item is selected from that list, thus leaving only the detail view visible. What is the correct way to hide the primary view on selection and then to optionally hide the supplemental view on selection using the new NavigationStack? (Notes is a good example of the sidebars hiding after the user selects a folder in the sidebar.)
Replies
1
Boosts
2
Views
1.5k
Activity
Jun ’22