Consider code
@EnvironmentObject var navModel: NavigationModel
var body: some View {
someView
.navigationDestination(for: ImageModel.self) { imageModel in
ImageDetailedView(image: imageModel)
.environmentObject(navModel) //this is required
}
}
Is navigation not considered a child of the view? And if so, is it normal to keep throwing around environemntObjects around the navigation stack?
import Combine
import SwiftUI
enum Destination {
case firstPage
case secondPage
}
enum Category: Int, Hashable, CaseIterable, Identifiable, Codable {
case dessert
case pancake
case salad
case sandwich
var id: Int { rawValue }
var localizedName: LocalizedStringKey {
switch self {
case .dessert:
return "Dessert"
case .pancake:
return "Pancake"
case .salad:
return "Salad"
case .sandwich:
return "Sandwich"
}
}
}
@available(iOS 16.0, *)
class Coordinator3: ObservableObject {
@Published var path = NavigationPath()
func gotoHomePage() {
path.removeLast(path.count)
}
func tapOnEnter() {
path.append(Destination.firstPage)
}
func tapOnFirstPage() {
path.append(Destination.secondPage)
}
func tapOnSecondPage() {
path.removeLast()
}
func test() {
path.removeLast(path.count)
path.append(2)
}
}
class Test :ObservableObject {
var name = "test"
}
@available(iOS 16.0, *)
struct SplitTestView: View {
@State var selectedCategory: Category?
var categories = Category.allCases
@ObservedObject var coordinator = Coordinator3()
@StateObject var test = Test()
var body: some View {
NavigationSplitView {
List(categories, selection: $selectedCategory) { category in
NavigationLink(category.localizedName, value: category)
}
} detail: {
NavigationStack(path: $coordinator.path) {
switch selectedCategory {
case .dessert:
Text(selectedCategory!.localizedName)
case .pancake:
VStack {
Text("Navigation stack")
.padding()
NavigationLink("NavigationLink to enter first page", value: Destination.firstPage)
.padding()
NavigationLink("NavigationLink to enter second page", value: Destination.secondPage)
.padding()
}
.navigationDestination(for: Destination.self) { destination in
if destination == .firstPage {
FirstPage()
} else {
Text(
"SecondPage()"
)
}
}
case .salad: Text(selectedCategory!.localizedName)
case .sandwich: Text(selectedCategory!.localizedName)
case .none: Text("hi")
}
}.environmentObject(test)
}
}
}
@available(iOS 16.0, *)
struct SplitTestView_Previews: PreviewProvider {
static var previews: some View {
SplitTestView()
}
}
struct FirstPage: View {
@EnvironmentObject var test: Test
var body: some View {
Text("First Page \(test.name)")
}
}
This is why MREs are important that is why I mentioned it in my first comment, you introduced NavigationSplitView.
Scenario 1
If you are using NavigationSplitView you have to inject the EnvironmentObject to the NavigationSplitView.
NavigationSplitView{
/*other stuff that includes a navigationDestination*/
}.environmentObject(navModel)
Scenerio 2
When working with just NavigationStack you have to inject on the NavigationStack
NavigationStack{
/*other stuff that includes a navigationDestination*/
}.environmentObject(navModel)
Scenerio 3 - Deprecated
When working with just NavigationView you have to inject on the NavigationView
NavigationView{
/*other stuff that includes a NavigationLink*/
}.environmentObject(navModel)
Your Sample
Just move the injection code one line down.
import Combine
import SwiftUI
@available(iOS 16.0, *)
struct SplitTestView: View {
@State var selectedCategory: Category?
var categories = Category.allCases
@StateObject var coordinator = Coordinator3() //<-- Switch to StateObject
@StateObject var test = Test()
var body: some View {
NavigationSplitView {
List(categories, selection: $selectedCategory) { category in
NavigationLink(category.localizedName, value: category)
}
} detail: {
NavigationStack(path: $coordinator.path) {
switch selectedCategory {
case .dessert:
Text(selectedCategory!.localizedName)
case .pancake:
VStack {
Text("Navigation stack")
.padding()
NavigationLink("NavigationLink to enter first page", value: Destination.firstPage)
.padding()
NavigationLink("NavigationLink to enter second page", value: Destination.secondPage)
.padding()
}
.navigationDestination(for: Destination.self) { destination in
if destination == .firstPage {
FirstPage()
} else {
Text(
"SecondPage()"
)
}
}
case .salad: Text(selectedCategory!.localizedName)
case .sandwich: Text(selectedCategory!.localizedName)
case .none: Text("hi")
}
}
}.environmentObject(test) //<<--- Add to the NavigationSplitView - The NavigationLink's are presenting in a separate column than the Stack, the only thing they share is the split view.
}
}
enum Destination {
case firstPage
case secondPage
}
enum Category: Int, Hashable, CaseIterable, Identifiable, Codable {
case dessert
case pancake
case salad
case sandwich
var id: Int { rawValue }
var localizedName: LocalizedStringKey {
switch self {
case .dessert:
return "Dessert"
case .pancake:
return "Pancake"
case .salad:
return "Salad"
case .sandwich:
return "Sandwich"
}
}
}
@available(iOS 16.0, *)
class Coordinator3: ObservableObject {
@Published var path = NavigationPath()
func gotoHomePage() {
path.removeLast(path.count)
}
func tapOnEnter() {
path.append(Destination.firstPage)
}
func tapOnFirstPage() {
path.append(Destination.secondPage)
}
func tapOnSecondPage() {
path.removeLast()
}
func test() {
path.removeLast(path.count)
path.append(2)
}
}
class Test :ObservableObject {
var name = "test"
}
@available(iOS 16.0, *)
struct SplitTestView_Previews: PreviewProvider {
static var previews: some View {
SplitTestView()
}
}
struct FirstPage: View {
@EnvironmentObject var test: Test
var body: some View {
Text("First Page \(test.name)")
}
}
Addl Info
In the Migration Guide apple talks about the differences between the 2 types.
https://developer.apple.com/documentation/swiftui/migrating-to-new-navigation-types
They call the inside of the NavigationStack "content"
NavigationStack {
/* content */
}
And the inside of the NavigationSplitView "columns"
NavigationSplitView {
/* column 1 */
} content: {
/* column 2 */
} detail: {
/* column 3 */
}
In their respective setups the "columns" and "content" only share the NavigationSplitView or NavigationStack respectively with the NavigationLinks/navigationDestination.
A NavigationLink inside NavigationStack that is inside NavigationSplitView presents in its own column.
The injection should always happen at the uppermost shared View.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With