4

I understand its new, but this seems like pretty basic functionality that is not here. When implementing a .searchable in the new iOS 15, it would seem that a NavigationLink does not work, at all.

Ideally, the searchable would produce a filtered list with a ForEach and for each item, a Nav Link could take you to another view based on your selection. The ForEach and list works and looks beautiful, it just wont take you anywhere.

Watching WWDC21, they talk an awful lot about .searchable, but very little demonstration/example is given..

Here is a simple example, with no ForEach loop, that shows it does not work at all.. Am I missing something?

Any insight appreciated:

import SwiftUI

struct ContentView: View {
    
    @State private var term = ""
    
    var body: some View {
        NavigationView {
            Text("Hello, world!")
                .padding()
        }
        .searchable(text: $term) {
            NavigationLink(destination: SecondView()) {
                Text("Go to second view")
            }
        }
    }
}

struct SecondView: View {
    var body: some View {
        Text("Goodbye!")
            .padding()
    }
}
JerseyDevel
  • 1,334
  • 1
  • 15
  • 34

2 Answers2

2

NavigationLink must always be inside NavigationView, no matter what. If you want to put it outside, like inside .searchable, you should use programmatic navigation with isActive.

struct ContentView: View {
    
    @State private var term = ""
    @State private var isPresenting = false
    
    var body: some View {
        NavigationView {
            
            /// NavigationView must only contain 1 view
            VStack {
                Text("Hello, world!")
                    .padding()
                
                /// invisible NavigationLink
                NavigationLink(destination: SecondView(), isActive: $isPresenting) { EmptyView()}
            }
        }
        .searchable(text: $term) {
            Button { isPresenting = true } label: {
                Text("Go to second view")
            }
        }
    }
}

struct SecondView: View {
    var body: some View {
        Text("Goodbye!")
            .padding()
    }
}

Result:

Working navigation link that only appears when search bar tapped

aheze
  • 24,434
  • 8
  • 68
  • 125
  • Ok, that makes sense, thank you. So if you happen to have a large ForEach listing of choices, I guess you'd have to use Buttons to set variables based on the user's selection and then trigger the NavLink which is inside the NavView.. – JerseyDevel Jun 10 '21 at 14:58
  • 1
    @Lkabo exactly. Use a list of buttons which set the NavigationLink. You'll probably need another variable to keep track of which button was pressed. – aheze Jun 10 '21 at 15:15
  • 1
    @Lkabo, please take a look at my answer here - https://stackoverflow.com/a/71312763/8068432, and see if it helps :) – Debaditya Sarkar Mar 01 '22 at 18:01
0

my 2 cents using data form Apple docs.

Some notes:

  1. You have to embrace list inside a NavigationView (NavigationStack for iOS17, as NavigationView is deprecated)

  2. even if Apple docs (and many on web) seem say so, without Nav..XX done not work: try yourself commenting out Nav..XX

     import SwiftUI
    
     struct Ocean: Identifiable {
         let name: String
         let id = UUID()
     }
    
     struct ContentView: View {
    
    
     private var oceans = [
         Ocean(name: "Pacific"),
         Ocean(name: "Atlantic"),
         Ocean(name: "Indian"),
         Ocean(name: "Southern"),
         Ocean(name: "Arctic")
     ]
    
     @State private var searchText = ""
    
     var body: some View {
         NavigationStack { // Try to commenting out.
    
             let filtered = self.oceans.filter { (ocean : Ocean) in
                 return ocean.name.uppercased().contains(searchText.uppercased()) || searchText.isEmpty
             }
             List(filtered) { (ocean: Ocean) in
                 Text(ocean.name)
             }
             .searchable(text: $searchText, prompt: "")
         }
     }
    

    }

ingconti
  • 10,876
  • 3
  • 61
  • 48