I'm trying to to implement a search on a datamodel via a searchbar.
My data model is the following struct:
struct NoteItem: Codable, Hashable, Identifiable {
let id: UUID
var text: String
var date = Date()
var dateText: String {
let df = DateFormatter()
df.dateFormat = "EEEE, MMM d yyyy, h:mm a"
return df.string(from: date)
}
var tags: [String] = []
var filename: String = ""
var changed: Bool = false
}
The application iterates on each element on the data model and populates a list, with a NavigationLink
that has two Text
: one with the first line of a TextEditor
, and another with the datamodel.dateText
. Each item in the list points to another view where the TextEditor
displays the full datamodel.text
The search I'm trying to add would be only on datamodel.text
. The code is as follow:
struct AllNotes: View {
@EnvironmentObject private var data: DataModel
...
var body: some View {
NavigationView {
List(data.notes) { note in
NavigationLink(
destination: NoteView(note: note, text: note.text),
tag: note.id,
selection: $selectedNoteId
) {
VStack(alignment: .leading) {
Text(getTitle(noteText: note.text)).font(.body).fontWeight(.bold)
Text(note.dateText).font(.body).fontWeight(.light)
}
.padding(.vertical, 10)
}
}
.listStyle(InsetListStyle())
.frame(minWidth: 250, maxWidth: .infinity)
}
.toolbar {
ToolbarItem(placement: .automatic) {
TextField("Search...", text: $searchText)
.textFieldStyle(RoundedBorderTextFieldStyle())
.frame(minWidth: 200)
}
}
}
}
The searchbar is a TextField in the toolbar.
In iOS, in the past, I've done something similar to this:
List(ListItems.filter({ searchText.isEmpty ? true : $0.name.contains(searchText) })) { item in
Text(item.name)
}
But given that the searchbar is an item in the toolbar, and that I need to filter / repopulate the list with the search from all the text in the data model, not just the list, how would I even begin to to that? How to filter it? Do I need to populate a new array? How can I access the text on the searchbar? is it $searchText
in the code I posted? Do I perform data.filter
?
I've ran out of ideas to try.
Thank you.
EDIT:
I have this sort of working, but note really. It messes up how the NavigationLink
are displayed, it adds random white spaces before the text.
code:
var body: some View {
NavigationView {
List {
ForEach(data.notes.filter { $0.text.contains(searchText) || searchText.isEmpty }) { note in
NavigationLink(
destination: NoteView(note: note, text: note.text),
tag: note.id,
selection: $selectedNoteId
) {
VStack(alignment: .leading) {
Text(getTitle(noteText: note.text)).font(.body).fontWeight(.bold)
Text(note.dateText).font(.body).fontWeight(.light)
}
.padding(.vertical, 10)
}
}
.listStyle(InsetListStyle())
.frame(minWidth: 250, maxWidth: .infinity)
.alert(isPresented: $showAlert, content: {
alert
})
Text("Create a new note...")
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
CodePudding user response:
Here's the answer edited to so people don't complain:
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(getTitle(noteText: note.text)).font(.body).fontWeight(.bold)
Text(note.dateText).font(.body).fontWeight(.light)
}
.padding(.vertical, 10)
}
}
.listStyle(InsetListStyle())
.frame(minWidth: 250, maxWidth: .infinity)
.alert(isPresented: $showAlert, content: {
alert
})
}