Trying to load a view in a loop using NavLink but when I click on the label, it just goes to a black screen for a few seconds and goes back to the root view. The code compiles fine in preview and there are no error codes. When I run the simulator I get this message in the debugger:
A NavigationLink is presenting a value of type “Missions” but there is no matching navigationDestination declaration visible from the location of the link. The link cannot be activated.
I have tried moving the navDest to different points within the navStack but it doesn't solve the problem. Code is:
struct ContentView: View {
@State private var gridList = true
let missions = MissionC()
let layout = [
GridItem(.adaptive(minimum: 150))
]
var body: some View {
NavigationStack {
Group {
if gridList {
ScrollView {
LazyVGrid(columns: layout) {
ForEach(missions.missionItem) { mission in
NavigationLink(value: mission) {
NavLink(mission: mission)
}
}
}
.navigationDestination(for: Missions.self) { mission in
MissionDetails(missions: mission)
}
.padding([.horizontal, .bottom])
}
} else {
List {
ForEach(missions.missionItem) {mission in
NavigationLink(value: mission) {
NavLink2(mission: mission)
}
}
.listRowBackground(Color.lightBackground)
}
.navigationDestination(for: Missions.self) { mission in
MissionDetails(missions: mission)
}
.scrollContentBackground(.hidden)
}
}
.navigationTitle("Moonshot")
.background(.darkBackground)
.preferredColorScheme(.dark)
.toolbar {
Button(gridList ? "Grid View" : "List View", systemImage: gridList ? "rectangle.grid.2x2": "list.bullet") {
gridList.toggle()
}
}
}
}
}
Missions struct and MissionC class are:
struct Missions: Identifiable, Codable, Hashable {
struct Crew: Codable, Hashable {
let name: String
let role: String
}
let id: Int
let launchDate: Date?
let crew: [Crew]
let description: String
var displayName: String {
"Apollo \(id)"
}
var image: String {
"apollo\(id)"
}
var getLaunchDate: String {
guard let a = launchDate else {
return "N/A"
}
return "\(a.formatted(date: .abbreviated, time: .omitted))"
}
}
class MissionC {
var missionItem: [Missions] = Bundle.main.decode("missions")
}
Been trying to figure this out since yesterday, rebuilt the content view code from scratch and didn't find anything wrong. Tried loading in the errant view a bunch of other ways and it worked so I don't think the problem is there. Also, tried a different view within the NavLink and it worked so I am stumped. Seems there is something weird going on in the interaction between that specific type of NavLink and that specific view. Also tried cleaning cache, build folder, deleting derivatives etc. None of which worked.
Edit: Adding in the relevant views. MissionDetails is:
struct MissionDetails: View {
var missions: Missions
let astronauts = AstronautC()
@State private var showingAlert = false
@State private var alertTitle = ""
@State private var alertMessage = ""
@State private var frostedOver = false
var body: some View {
NavigationStack {
ZStack {
ScrollView {
VStack {
Image(missions.image)
.resizable()
.scaledToFit()
.containerRelativeFrame(.horizontal) {size, axis in
size * 0.8
}
HStack{
Text("Launch date:")
.font(.title3)
.bold()
Text(missions.getLaunchDate)
}
}
.padding(.top)
rectangle()
VStack(alignment: .leading) {
Spacer()
Text(missions.description)
.padding(.horizontal)
rectangle()
}
Text("Crew")
.font(.title3)
.bold()
VStack(alignment: .leading) {
ForEach(missions.crew, id: \.self) { crew in
Image(crew.name)
.resizable()
.scaledToFit()
.clipShape(.capsule)
.overlay(
Capsule()
.strokeBorder(.white, lineWidth: 1)
)
let person = astronauts.astronautItem[crew.name]
Button {
alertTitle = "\(person?.name ?? "")"
alertMessage = "\(person?.description ?? "")"
withAnimation {
frostedOver = true
}
showingAlert = true
} label: {
Text(person?.name ?? "")
.font(.headline)
}
Text(crew.role)
}
}
.padding([.horizontal, .bottom])
}
.navigationTitle(missions.displayName)
.navigationBarTitleDisplayMode(.inline)
.alert(alertTitle, isPresented: $showingAlert) {
Button("Ok") {
withAnimation {
frostedOver = false
}
showingAlert = false
}
} message: {
Text(alertMessage)
}
.background(.darkBackground)
if frostedOver {
Color(.clear)
.background(.ultraThinMaterial)
.ignoresSafeArea()
}
}
}
}
}
#Preview {
let missions = MissionC()
MissionDetails(missions: missions.missionItem[0])
.preferredColorScheme(.dark)
}
NavLink is:
struct NavLink: View {
var mission: Missions
var body: some View {
VStack {
Image(mission.image)
.resizable()
.scaledToFit()
.frame(width: 100, height: 100)
.padding()
VStack {
Text(mission.displayName)
.font(.headline)
.foregroundStyle(.white)
Text(mission.getLaunchDate)
.font(.caption)
.foregroundStyle(.gray)
}
.padding(.vertical)
.frame(maxWidth: .infinity)
.background(.lightBackground)
}
.clipShape(.rect(cornerRadius: 8))
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(.lightBackground)
)
}
}
#Preview {
let preview = MissionC()
NavLink(mission: preview.missionItem[0])
}
NavLink2 is:
struct NavLink2: View {
var mission: Missions
var body: some View {
HStack(spacing: 40) {
Image(mission.image)
.resizable()
.scaledToFit()
.frame(width: 60, height: 60)
.padding()
VStack {
Text(mission.displayName)
.font(.headline)
.foregroundStyle(.white)
Text(mission.getLaunchDate)
.font(.caption)
.foregroundStyle(.gray)
}
}
}
}
#Preview {
let preview = MissionC()
NavLink2(mission: preview.missionItem[0])
.preferredColorScheme(.dark)
}
The error you're getting clearly explains it. The navigation link is looking for a .navigationDestination
in the NavigationStack of MissionDetails
(in order to resolve the value), and there isn't any.
To fix it, remove the extra NavigationStack
from MissionDetails
.
Since MissionDetails has a parent NavigationStack, you can still use .navigationTitle
just fine.