Having an issue with the above error encountered when attempting to trigger a Navigation Link Programatically. Upon clicking the link, the wrong destination array element will load sporadically (vs the label clicked on), and of course the above error shows.
When I trigger the Navigation Link non programmatically the error does not occur, but I have another method that I need to run when the user clicks this link.
I have read through several questions, like here and here, but cannot find the cause in my case.
Model Struct:
struct Place: Identifiable {
var id: UUID = UUID()
var name: String = ""
var testBool: Bool = false
}
Class for environment object: (Production project has many properties and methods - essentially this is the ViewModel)
class UserPlaces: ObservableObject {
@Published var placesArray: [Place] = [Place]([Place(name: "Christian", testBool: false),Place(name: "Catherine", testBool: true)])
}
Class for navigation boolean: (Production project has many properties here that I want to keep in one class)
class Navigation: ObservableObject {
@Published var showPlaceDetail: Bool = false
}
ContentView: (Parent view where NavigationView is located)
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
ScrollView {
Text("Hello, world!")
.padding()
CardsGrid2()
} // End ScrollView
} // End Navigation View
} // End body
} // End Struct
CardsGrid: (In this view the NavigationLink is not programmatically triggered and the console error does not show.)
import SwiftUI
struct CardsGrid: View { @EnvironmentObject var userPlaces: UserPlaces
var gridLayout: [GridItem] = [GridItem(.flexible(minimum: 40), spacing: -36),GridItem(.flexible(minimum: 40), spacing: -36)]
var body: some View {
LazyVGrid(columns: gridLayout, spacing: 10) {
ForEach(userPlaces.placesArray) { placeCard in
NavigationLink(
destination: Text("Destination")
, label: { Text("Label") }
)
} // End ForEach
} // End LazyVGrid
} // End body
} // End struct
CardsGridProgrammatic: (In this view the NavigationLink is programmatically triggered and the console error shows.)
import SwiftUI
struct CardsGridProgrammatic: View {
@EnvironmentObject var userPlaces: UserPlaces
@EnvironmentObject var navigation: Navigation
var gridLayout: [GridItem] = [GridItem(.flexible(minimum: 40), spacing: -36),GridItem(.flexible(minimum: 40), spacing: -36)]
var body: some View {
LazyVGrid(columns: gridLayout, spacing: 10) {
ForEach(userPlaces.placesArray) { placeCard in
NavigationLink(destination: Text("Destination"), isActive: $navigation.showPlaceDetail) { EmptyView() }
.isDetailLink(false)
Button(action: {
navigation.showPlaceDetail = true
// Other required method call here
}, label: { Text("label") })
} // End ForEach
} // End LazyVGrid
} // End body
} // End struct
Additionally:
How can I fix this?
As your links suggested move the NavigationLink
out of the ForEach
.
The following works well for me on macos 12.01, xcode 13.1, target ios 15 and macCatalyst 12.
Tested on mac Monterey and iPhone ios 15 devices. It may be different on older systems.
To be sure, this is the code I used for testing:
import SwiftUI
@main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct Place: Identifiable {
var id: UUID = UUID()
var name: String = ""
var testBool: Bool = false
}
class UserPlaces: ObservableObject {
@Published var placesArray: [Place] = [
Place(name: "Christian", testBool: false),
Place(name: "Catherine", testBool: true)]
}
class Navigation: ObservableObject {
@Published var showPlaceDetail: Bool = false
}
struct ContentView: View {
@StateObject var userPlaces = UserPlaces()
@StateObject var navigation = Navigation()
var body: some View {
NavigationView {
ScrollView {
Text("Hello, world!").padding()
CardsGrid()
}
}
.environmentObject(userPlaces)
.environmentObject(navigation)
}
}
struct CardsGrid: View {
@EnvironmentObject var userPlaces: UserPlaces
@EnvironmentObject var navigation: Navigation
@State var selection: Place? // <--- here
var gridLayout: [GridItem] = [GridItem(.flexible(minimum: 40), spacing: -36),
GridItem(.flexible(minimum: 40), spacing: -36)]
var body: some View {
LazyVGrid(columns: gridLayout, spacing: 10) {
ForEach(userPlaces.placesArray) { placeCard in
Button(action: {
selection = placeCard // <--- here
navigation.showPlaceDetail = true
// Other required method call here
}, label: { Text("\(placeCard.name) label") })
}
}
// --- here
NavigationLink(destination: Text("Destination \(selection?.name ?? "")"),
isActive: $navigation.showPlaceDetail) { EmptyView() }.isDetailLink(false)
}
}