Showing ProgressView during a search

221 views Asked by At

NOTE: this question is not about how to use .searchable or how to filter a List.

I am using the following view to search an external database:

struct SearchDatabaseView: View {
    @Environment(\.dismiss) private var dismiss
    @Environment(\.isSearching) private var isSearching: Bool

    @State private var searchText: String = ""
    @State private var searchResults: [Record] = []

    var body: some View {
        NavigationStack {
            List(searchResults, id: \.self) { record in
                /// display results here
            }
            .navigationTitle("Search Database")
            .toolbar {
                Button(action: {
                    dismiss()
                }) {
                    Text("Done")
                }
            }
            .overlay {
                if isSearching {
                    ProgressView("Searching Database...")
                }
            }
        }
        .searchable(text: $searchText)
        .disableAutocorrection(true)

        .onSubmit(of: .search) {
            searchDatabase()
        }
    }
}

Everything works, except the progress view is not showing. I tried putting the .overlay modifier after .onSubmit, but still it doesn't show.

What am I missing, is that not the proper use of isSearching ?

1

There are 1 answers

2
workingdog support Ukraine On BEST ANSWER

Try this approach, where two views are used (like the docs examples) to perform the search and dismissal using dismissSearch and display the ProgressView.

This is just an example code, see the docs at: https://developer.apple.com/documentation/swiftui/managing-search-interface-activation for more comprehensive info and examples.

struct ContentView: View {
    var body: some View {
        SearchDatabaseView()
    }
}

struct SearchDatabaseView: View {
    @State private var searchText: String = ""

    var body: some View {
        NavigationStack {
            ListView()
            .searchable(text: $searchText)
            .disableAutocorrection(true)
            .navigationTitle("Search Database")
            .onSubmit(of: .search) {
                // searchDatabase()
                print("----> onSubmit: \(searchText)")
            }
        }
    }
}

struct ListView: View {
    @Environment(\.dismissSearch) private var dismissSearch
    @Environment(\.isSearching) private var isSearching
    
    @State private var searchResults: [String] = ["a-record", "b-record", "c-record", "d-record"]
    
    var body: some View {
        List(searchResults, id: \.self) { record in
            Text(record)
        }
        .toolbar {
            Button("Done") {
                dismissSearch()
            }
                .overlay {
                    if isSearching {
                        ProgressView("Searching Database...")
                    }
                }
        }
    }
}

EDIT-1:

To cater for your new question, I would do away with the isSearching thing. Use a "normal" variable and implement a simple but effective code structure, such as in this example code:

struct SearchDatabaseView: View {
    @State private var searchText: String = ""
    @State private var showSearching = false
    
    @State private var searchResults: [String] = ["a-record", "b-record", "c-record", "d-record"]
    
    var body: some View {
        NavigationStack {
            List(searchResults, id: \.self) { record in
                Text(record)
            }
            .toolbar {
                Button("Done") {
                    showSearching = false
                }
                .overlay {
                    if showSearching {
                        ProgressView("Searching Database...")
                    }
                }
                .searchable(text: $searchText)
                .disableAutocorrection(true)
                .navigationTitle("Search Database")
                .onSubmit(of: .search) {
                    showSearching = true
                    // searchDatabase()
                    // simulation of searchDatabase(), could also pass showSearching to it
                    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                        // .....
                        showSearching = false  // when finished searchDatabase()
                    }
                }
            }
        }
    }
}