I'm trying to understand if there is a way to use NavigationSplitView
where the content
view is not a List
. I want to have a custom content
view without a List
. The problem, however, is that if I don't use a List
, the detail view is not presented on iPhone.
Here's my starting point code - which works fine, i.e. the detail view slides in on the iPhone.
import SwiftUI
struct ContentView: View {
@State private var selectedCategory: Category?
@State private var selectedRecipe: Recipe?
var body: some View {
NavigationSplitView {
List(Category.allCases, selection: $selectedCategory) { category in
NavigationLink(value: category) {
Text(category.rawValue)
}
}
} content: {
if let selectedCategory {
List(Recipe.allCases, selection: $selectedRecipe) { recipe in
NavigationLink(value: recipe) {
Text(recipe.rawValue)
}
}
} else {
Text("Nothing selected")
}
} detail: {
if let selectedRecipe {
Text(selectedRecipe.rawValue)
} else {
Text("Nothing selected")
}
}
}
}
enum Category: String, CaseIterable, Identifiable {
var id: String {
self.rawValue
}
case main
case settings
}
enum Recipe: String, CaseIterable, Identifiable {
var id: String {
self.rawValue
}
case omelet
case cereal
}
Now, here's a modification that allows me to remove the unneeded (in my case) NavigationLink
. This code also works fine. The button (when tapped) can update the selectedRecipe
which causes the detail view to slide in on the iPhone.
...
List(Recipe.allCases, selection: $selectedRecipe) { recipe in
// NavigationLink(value: recipe) {
//
// Text(recipe.rawValue)
// }
Button {
self.selectedRecipe = recipe
} label: {
Text(recipe.rawValue)
}
}
...
But since I don't need either a List
or a NavigationLink
in my custom content
view, I tried this (simplified code). This works on the iPad, where the detail
area displays the selectedRecipe
, but on an iPhone, the detail
view doesn't slide in and replace the content
view.
...
Button {
self.selectedRecipe = Recipe.omelet
} label: {
Text(Recipe.omelet.rawValue)
}
...
Anyway, I'm just confused about whatever magic is happening that requires a List
with a selection
binding to make setting the self.selectedRecipe
actually cause the detail view to appear on an iPhone.
Any guidance appreciated.
NavigationSplitView
cannot be "driven" by anything other than List
. See also this discussion on Apple Developer Forums.
That said, I've found that adding a navigationDestination
to the sidebar/content column does cause a navigation to the detail column. For example:
} content: {
if let selectedCategory {
Button {
self.selectedRecipe = Recipe.omelet
} label: {
Text(Recipe.omelet.rawValue)
}
.navigationDestination(item: $selectedRecipe) {
Text($0.rawValue)
}
} else {
Text("Nothing selected")
}
} detail: {
// the detail parameter here is only used for the case when nothing is selected
// the actual detail view goes in the navigationDestination above
Text("Nothing selected")
}
I don't think this is an intended usage of navigationDestination
though, as its documentation does not mention that it can be used in a NavigationSplitView
. The fact that this works could just be a coincidence, due to some implementation detail.
Another workaround is to just hide a List
with opacity(0)
} content: {
if let selectedCategory {
ZStack {
List(Recipe.allCases, selection: $selectedRecipe) { _ in
Text("")
}
.opacity(0)
Button {
self.selectedRecipe = Recipe.omelet
} label: {
Text(Recipe.omelet.rawValue)
}
}
} else {
Text("Nothing selected")
}
}