NavigationLink inside .searchable does not work

841 Views Asked by At

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()
    }
}
2

There are 2 best solutions below

3
On BEST ANSWER

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

1
On

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: "")
         }
     }
    

    }