'Using .searchable on a macOS causes the focus to always jump back to the search field

I'm trying to move away from having a TextField in the toolbar by using the new .searchable. But there seems to be a problem I can't solve. When you type the text you want to search, I can filter the list with that text, but when I place the mouse cursor on the first item and try to move down the list with the arrow key, with each arrow key press, the focus goes back to the search field, making it impossible to navigate up and down the list with the keyboard.

Maybe I'm not implementing it right, or maybe it doesn't work yet with macOS, either way, this is the code I'm using:

struct AllNotes: View {
   @EnvironmentObject private var data: DataModel
   @State var selectedNoteId: UUID?
   @State var searchText: String = ""

   var body: some View {
      NavigationView {
            List(data.notes.filter { searchText.isEmpty ? true : $0.text.localizedCaseInsensitiveContains(searchText) }) { note in
                NavigationLink(
                    destination: NoteView(note: note, text: note.text),
                    tag: note.id,
                    selection: $selectedNoteId
                ) {
                    VStack(alignment: .leading) {
                        Text(getFirstLine(noteText: note.text)).font(.body).fontWeight(.bold)
                    }
                }
            }
            .searchable(
                text: $searchText,
                placement: .toolbar,
                prompt: "Search..."
            )
            .listStyle(InsetListStyle())
            .toolbar {
                // a few other buttons
            }
      }
   }
}

The DataModel is simple a struct of NoteItem:

struct NoteItem: Codable, Hashable, Identifiable {
    let id: UUID
    var text: String
    var changed: Bool = false
}

Am I missing anything? Am I implementing this right?

EDIT:

Based on suggestions from Apple and other sites, .searchable should be added under the navigation view. So I moved that there. The default behavior, as described by Apple, of adding it to the end of the toolbar is still happening, but that's ok. However the problem still persists, the focus jumps back to the search field each time you click on a list item.

struct AllNotes: View {
   @EnvironmentObject private var data: DataModel
   @State var selectedNoteId: UUID?
   @State var searchText: String = ""

   var body: some View {
      NavigationView {
            List(data.notes.filter { searchText.isEmpty ? true : $0.text.localizedCaseInsensitiveContains(searchText) }) { note in
                NavigationLink(
                    destination: NoteView(note: note, text: note.text),
                    tag: note.id,
                    selection: $selectedNoteId
                ) {
                    VStack(alignment: .leading) {
                        Text(getFirstLine(noteText: note.text)).font(.body).fontWeight(.bold)
                    }
                }
            }
            .listStyle(InsetListStyle())
            .toolbar {
                // a few other buttons
            }
      }
      .searchable(
          text: $searchText,
          placement: .toolbar,
          prompt: "Search..."
      )
 
   }
}


Solution 1:[1]

I think the problem is because you are showing the list in the sidebar but have the search field in the toolbar. So you could try moving the search field to the sidebar which does fix the problem with navigating items with arrow keys but I wasn't able to tab back to the search field. And InsetListStyle didn't seem compatible with searching so I commented that. And by the way, you are missing the default detail view for your NavigationView so you need to add that. Also your View structure needed tweaked so you pass the filtered results into the child View E.g.

struct NoteView: View {
    let note: NoteItem
    //let text: String
    var body: some View {
        Text(note.text)
    }
}

struct NotesView: View {
    @State private var selectedNoteId: UUID?
    let notes: [NoteItem]
    
    var body: some View {
        List(notes) { note in
            NavigationLink(
                destination: NoteView(note: note), //text: note.text),
                tag: note.id,
                selection: $selectedNoteId
            ) {
                VStack(alignment: .leading) {
                    Text(note.text).font(.body).fontWeight(.bold)
                }
            }
        }
       // .listStyle(InsetListStyle())
    }
}

struct SearchView: View {
    @EnvironmentObject private var data: DataModel
    @State var searchText: String = ""

   var body: some View {
      NavigationView {
          NotesView(notes: filteredNotes)
          Text("Make a selection")
//            .toolbar {
//                // a few other buttons
//            }
          
      }
      .searchable(
          text: $searchText,
          placement: .sidebar,
          prompt: "Search..."
      )
   }
    
    var filteredNotes: [NoteItem] {
        data.notes.filter { searchText.isEmpty ? true : $0.text.localizedCaseInsensitiveContains(searchText)
        }
    }
}



struct ContentView: View {
    @StateObject var model = DataModel()
    var body: some View {
        SearchView()
            .environmentObject(model)
    }
}

class DataModel: ObservableObject {
    @Published var notes: [NoteItem] = [NoteItem(text: "Test1"), NoteItem(text: "Test2")]
}

struct NoteItem: Codable, Hashable, Identifiable {
    let id = UUID()
    var text: String
    var changed: Bool = false
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 malhal