SwiftUI NavigationLink in List with subviews

Hello,

I have a list in the root view of my app like so:

Code Block
List {
NavigationLink(destination: AnimalListView()) {
SummaryView()
}
}


And SummaryView is a row of boxes, like so:
Code Block
HStack {
VStack {
Text("24")
Text("Dogs")
}
VStack {
Text("10")
Text("Cats")
}
// etc...
}


The root view is a count of each animal type.
The detail view is a listing of animals, filterable by type.

Can I make it so that if the user taps a specific box, it takes them to the detail view with the filter already set for the type in the box they tapped, but if they tap on the row itself, it goes there without the filter set?

I can't figure out how to get the type or tap location from the summary view, up into the root view, to trigger a NavigationLink.

Thank you



Answered by OOPer in 655877022

Here's more code:

When you show some code to help readers help you, please write a complete code.
Your structs lack : View and there is no NavigationView, so NavigationLink has no meaning.

Please take some time to write a complete code, create a brand new project and add all your code to it.
And then check it compiles or not, if it compiles, check if it can represent the context you want to improve.

Doing so would save readers time, which means your will get better answers sooner.

if the user taps on the row background, ... if the user taps on the Text items

You mean you force users to distinguish the background and the Text items without showing any indication of it???
What an ...


Anyway, you may need to handle navigations manually by putting onTapGesture to each item you want to detect taps:
Code Block
struct ContentView: View {
@State var animalType: AnimalListView.AnimalType = .allAnimals
@State var linkIsActive: Bool = false
var body: some View {
NavigationView {
List {
NavigationLink(
destination: AnimalListView(animalType: animalType)
.onDisappear{animalType = .allAnimals},
isActive: $linkIsActive
) {
SummaryView(animalType: $animalType, linkIsActive: $linkIsActive)
}
}
}
}
}
struct SummaryView: View {
@Binding var animalType: AnimalListView.AnimalType
@Binding var linkIsActive: Bool
var body: some View {
HStack {
VStack {
Text("24")
Text("Dogs")
}
//.background(Color.red)
.onTapGesture {
//print("Dogs")
animalType = .dog
linkIsActive = true
}
VStack {
Text("24")
Text("Cats")
}
//.background(Color.orange)
.onTapGesture {
//print("Cats")
animalType = .cat
linkIsActive = true
}
}
}
}
struct AnimalListView: View {
enum AnimalType {
case cat
case dog
case allAnimals
}
@State var animalType: AnimalType
var body: some View {
// list animals of selected type
Text("list animals of \(String(describing: animalType))")
}
}


If the actual view hierarchy of your app is different than you have shown, you may need to modify many parts.
Hard to say something under the currently shown info.
  • How filterable is implemented?

  • How we can know the filter already set for the type?

  • How we can distinguish tapping a box and tapping a row?

Better show whole code than showing very limited fragments.
Sorry, I was trying to make the example as easy to follow as possible, apparently that backfired.

Here's more code:

Root view:
Code Block
struct ContentView {
var body: some View {
List {
NavigationLink(destination: AnimalListView()) {
SummaryView()
}
}
}
}


Summary view:
Code Block
struct SummaryView {
var body: some View {
HStack {
VStack {
Text("24")
Text("Dogs")
}
}
HStack {
VStack {
Text("24")
Text("Cats")
}
}
}
}


AnimalListView:
Code Block
struct AnimalListView {
enum AnimalType {
case cat
case dog
case allAnimals
}
@State var animalType: AnimalType
var body: some View {
// list animals of selected type
}
}


By filter already set, I mean that I want to have animalType set to .allAnimals if the user taps on the row background, or a specific animal type if the user taps on the Text items in the summaryView. ie, the user taps on "24 Dogs", so the detail view should filter on .dog

Your third point is the basically the essence of my question. What is a sensible way to get the info of what the user tapped from SummaryView up into my ContentView so that the information can be used to set the filter when navigating to the AnimalListView?

Accepted Answer

Here's more code:

When you show some code to help readers help you, please write a complete code.
Your structs lack : View and there is no NavigationView, so NavigationLink has no meaning.

Please take some time to write a complete code, create a brand new project and add all your code to it.
And then check it compiles or not, if it compiles, check if it can represent the context you want to improve.

Doing so would save readers time, which means your will get better answers sooner.

if the user taps on the row background, ... if the user taps on the Text items

You mean you force users to distinguish the background and the Text items without showing any indication of it???
What an ...


Anyway, you may need to handle navigations manually by putting onTapGesture to each item you want to detect taps:
Code Block
struct ContentView: View {
@State var animalType: AnimalListView.AnimalType = .allAnimals
@State var linkIsActive: Bool = false
var body: some View {
NavigationView {
List {
NavigationLink(
destination: AnimalListView(animalType: animalType)
.onDisappear{animalType = .allAnimals},
isActive: $linkIsActive
) {
SummaryView(animalType: $animalType, linkIsActive: $linkIsActive)
}
}
}
}
}
struct SummaryView: View {
@Binding var animalType: AnimalListView.AnimalType
@Binding var linkIsActive: Bool
var body: some View {
HStack {
VStack {
Text("24")
Text("Dogs")
}
//.background(Color.red)
.onTapGesture {
//print("Dogs")
animalType = .dog
linkIsActive = true
}
VStack {
Text("24")
Text("Cats")
}
//.background(Color.orange)
.onTapGesture {
//print("Cats")
animalType = .cat
linkIsActive = true
}
}
}
}
struct AnimalListView: View {
enum AnimalType {
case cat
case dog
case allAnimals
}
@State var animalType: AnimalType
var body: some View {
// list animals of selected type
Text("list animals of \(String(describing: animalType))")
}
}


If the actual view hierarchy of your app is different than you have shown, you may need to modify many parts.
Thank you, the combination of isActive on NavigationLink and .onAppear together are what I had not understood.

With a couple of adaptations that will work nicely.

Most forums I frequent prefer pseudocode for brevity. I will remember that's not the case here - thanks for the heads up.

Most forums I frequent prefer pseudocode for brevity.

This is taken from the most famous Q&A site and it applies to the Dev Forums too.
How to create a Minimal, Reproducible Example



SwiftUI NavigationLink in List with subviews
 
 
Q