I am new to SwiftUI and I am simply experimenting with different NavigationStack options. I have a navigation stack that shows a list of words. When the user clicks on the navigation link it shows a detailed view of that word. Very simple.
The code below doesn't work. When I click on the link, it takes me to the a screen which just flashes and it goes directly to the parent view of the view below.
var body: some View {
NavigationStack {
List(dictionary, id: \.id) { wordDefinition in
NavigationLink(wordDefinition.g_keyword, value: wordDefinition)
}
.navigationDestination(for: DictionaryEntry.self) { wordDefinition in
DefinitionCardView(wordDefinition: wordDefinition)
}
.listStyle(PlainListStyle())
}
}
This code works fine.
var body: some View {
NavigationStack {
List(dictionary, id: \.id) { wordDefinition in
NavigationLink(wordDefinition.g_keyword, destination: DefinitionCardView(wordDefinition: wordDefinition))
}
.listStyle(PlainListStyle())
}
}
Parent view of the above code. I am just playing around with code so please ignore any logic or optimization issues. For example SomeViewThatActuallyDoesNothing() acts like a button that if pressed, takes you to the view in question. But that's just me trying different things and I don't consider this a good design choice. However, this illogical design choice presents the problem I am facing here and curiosity got me. Why does one version work and the other doesn't?
var body: some View {
NavigationStack {
VStack(alignment: .leading) {
SomeViewThatActuallyDoesNothing(searchWord: $searchWord)
.onTapGesture {
isSearchFieldTapped = true;
}
NavigationLink {
DefinitionCardView(wordDefinition: wordOfTheDay)
} label: {
SomeSampleCardView(wordOfTheDay: wordOfTheDay)
}
}
.padding()
.navigationDestination(isPresented: $isSearchFieldTapped) {
MainSearchView() // This is the view with the problem (the code above)
}
}
I am curios now why the first version is not working while the second is fine.
EDIT: Minimal Reproducible Example
import SwiftUI
import SwiftData
struct ContView: View {
var body: some View {
NavigationStack {
VStack(alignment: .center) {
Spacer()
NavigationLink {
MinNOTWorkingListView()
} label: {
Text("A View With a Problem")
}
NavigationLink {
MinWorkingListView()
} label: {
Text("A Working View")
}
Spacer()
NavigationLink {
MinCardView(wordDef: "wordOfTheDay")
} label: {
Text("Some Text")
}
Spacer()
}
.padding()
}
}
}
struct MinWorkingListView: View {
private var wordList: [String] = {
var list: [String] = []
for i in 1...5 {
list.append("Word" + String(i))
}
return list
}()
var body: some View {
NavigationStack {
List(wordList, id: \.self) { wordDefinition in
NavigationLink(wordDefinition, destination: MinCardView(wordDef: wordDefinition))
}
.listStyle(PlainListStyle())
}
}
}
struct MinNOTWorkingListView: View {
private var wordList: [String] = {
var list: [String] = []
for i in 1...5 {
list.append("Word" + String(i))
}
return list
}()
var body: some View {
NavigationStack {
List(wordList, id: \.self) { wordDefinition in
NavigationLink(wordDefinition, value: wordDefinition)
}
.navigationDestination(for: String.self) { wordDefinition in
MinCardView(wordDef: wordDefinition)
}
.listStyle(PlainListStyle())
}
}
}
struct MinCardView: View {
var wordDef: String
var body: some View {
Text(wordDef)
}
}
Just like mentioned in a comment by @Sweeper, you should not nest two NavigationStack
s.
Another thing, when mixing NavigationLink(destination:label:)
with NavigationLink(value:label:)
like that:
struct ContView: View {
var body: some View {
NavigationStack {
NavigationLink(destination: {
NavigationLink(value: "Value") {
Text("To SomeView")
}
}){
Text("Start")
}
.navigationDestination(for: String.self) { value in
SomeView(value: value)
}
}
}
}
..it seems to overwhelm the NavigationStack
(..and it might actually be a bug).
Solution:
You can use several NavigationLink(value:label:)
s with dedicated .navigationDestination(for:destination)
modifiers for each.
Alternatively you could introduce a new Destination
enum and handle its value in a .navigationDestination(for:destination)
with a switch
statement:
enum Destination: Hashable {
case list
case card(String)
}
struct ContView: View {
var body: some View {
NavigationStack {
NavigationLink(value: Destination.list) {
Text("List")
}
.navigationDestination(for: Destination.self) { destination in
switch destination {
case .list:
ListView()
case .card(let wordDefinition):
SomeView(value: wordDefinition)
}
}
}
}
}
struct ListView: View {
private var wordList: [String] = {
var list: [String] = []
for i in 1...5 {
list.append("Word" + String(i))
}
return list
}()
var body: some View {
List(wordList, id: \.self) { wordDefinition in
NavigationLink(wordDefinition, value: Destination.card(wordDefinition))
}
}
}