I’m trying to implement search performance just like that of the App Retailer on iOS, with the addition of search scopes. Many of the search performance is working appropriately, as customers can search and obtain search ideas based mostly on the at the moment chosen scope. Nevertheless, I’m going through a problem when a consumer searches throughout the ‘all’ scope and faucets on one of many ideas. On this case, I not solely need the search textual content to be accomplished with the tapped suggestion and the search to be executed, however I additionally need the at the moment chosen search scope to alter to the scope that matches the tapped suggestion.
I’ve tried including an ‘onTapGesture’ modifier, however this causes the ‘searchCompletion’ modifier to cease working. However, if I take away the ‘searchCompletion’ modifier and manually deal with every thing – setting the search textual content, altering the search scope, clearing the search ideas, and performing the search – the search ideas stay on the display.
Is there a technique to change the search scope when a consumer faucets on a search suggestion, or am I making an attempt to realize one thing that isn’t possible?
import SwiftUI
struct SearchView: View {
let citrus = ["Lemon", "Orange"]
let tropical = ["Banana", "Kiwi", "Mango", "Pineapple", "Banana2"]
let berry = ["Cherry", "Grape", "Strawberry"]
@State non-public var fruits: [String] = []
@State non-public var searchText = ""
@State non-public var searchScope = SearchScope.all
@State non-public var searchSuggestions: [SearchSuggestion] = []
var physique: some View {
NavigationStack {
Checklist {
ForEach(fruits, id: .self) { fruit in
Textual content(fruit)
}
}
.navigationTitle("Fruit Search")
}
.searchable(textual content: $searchText) {
ForEach(searchSuggestions, id: .self) { suggestion in
Textual content("(suggestion.fruit) in (suggestion.scope.rawValue.capitalized)")
// .searchCompletion(suggestion.fruit)
.onTapGesture {
searchText = suggestion.fruit
searchScope = suggestion.scope
searchSuggestions = []
runSearch()
}
}
}
// Why is the activation wanted? With out it the picker is not displayed...
.searchScopes($searchScope, activation: .onSearchPresentation) {
ForEach(SearchScope.allCases, id: .self) { scope in
Textual content(scope.rawValue.capitalized)
}
}
.onAppear(carry out: runSearch)
.onSubmit(of: .search, runSearch)
.onChange(of: searchScope) {
fetchSearchSuggestions()
}
.onChange(of: searchText) {
fetchSearchSuggestions()
}
}
func runSearch() {
if searchText.isEmpty {
fruits = citrus + tropical + berry
return
}
change searchScope {
case .all:
let allFruits = citrus + tropical + berry
fruits = allFruits.filter { $0.accommodates(searchText) }
case .citrus:
fruits = citrus.filter { $0.accommodates(searchText) }
case .tropical:
fruits = tropical.filter { $0.accommodates(searchText) }
case .berry:
fruits = berry.filter { $0.accommodates(searchText) }
}
}
func fetchSearchSuggestions() {
if searchText.isEmpty {
searchSuggestions =
citrus.map { SearchSuggestion(fruit: $0, scope: .citrus) }
+ tropical.map { SearchSuggestion(fruit: $0, scope: .tropical) }
+ berry.map { SearchSuggestion(fruit: $0, scope: .berry) }
return
}
change searchScope {
case .all:
let allFruits =
citrus.map { SearchSuggestion(fruit: $0, scope: .citrus) }
+ tropical.map { SearchSuggestion(fruit: $0, scope: .tropical) }
+ berry.map { SearchSuggestion(fruit: $0, scope: .berry) }
searchSuggestions = allFruits.filter { $0.fruit.accommodates(searchText) }
case .citrus:
searchSuggestions = citrus.filter { $0.accommodates(searchText) }.map { SearchSuggestion(fruit: $0, scope: .citrus) }
case .tropical:
searchSuggestions = tropical.filter { $0.accommodates(searchText) }.map { SearchSuggestion(fruit: $0, scope: .tropical) }
case .berry:
searchSuggestions = berry.filter { $0.accommodates(searchText) }.map { SearchSuggestion(fruit: $0, scope: .berry) }
}
}
}
enum SearchScope: String, CaseIterable {
case all, citrus, tropical, berry
}
struct SearchSuggestion: Hashable {
let fruit: String
let scope: SearchScope
}