Home > OS >  Searchbar to filter contents on a list (macOS Big Sur)
Searchbar to filter contents on a list (macOS Big Sur)

Time:10-14

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

        }
  • Related