我需要使用搜索栏来过滤模型数据。我添加了.searchable()
属性,当搜索文本发生变化时,我使用模糊匹配来过滤对象。这花费了太多的时间,并且在写入搜索框时应用程序会延迟。所以我希望异步执行搜索,这样应用程序就不会冻结。
我试着用onChange(of:)
属性来做,然后我创建了一个运行异步函数的Task
,因为onChange()
属性本身不允许异步函数。
下面是一个代码示例,说明我是如何尝试这样做的:
import SwiftUI
import Fuse
struct SearchView: View {
@EnvironmentObject var modelData: ModelData
@State var searchText = ""
@State var searchResults: [Item] = []
@State var searchTask: Task<(), Never>? = nil
let fuseSearch = Fuse()
var body: some View {
// Show search results
}
.searchable(text: $searchText)
.onChange(of: searchText) { newQuery in
// Cancel if still searching
searchTask?.cancel()
searchTask = Task {
searchResults = await fuzzyMatch(items: modelData.items, searchText: newQuery)
}
}
func fuzzyMatch(items: [Item], searchText: String) async -> [Item] {
filteredItems = items.filter {
(fuseSearch.search(searchText, in: $0.name)?.score ?? 1) < 0.25
}
return filteredItems
}
}
我真的很感激你的帮助。
2条答案
按热度按时间9rbhqvlz1#
我认为主要的问题是debounging,就像lorem ipsum之前提到的那样。我刚刚测试了我的代码,你需要在我打印的地方调用你的filter方法。
这样你就不会过滤每一个编辑文本域。你会在几毫秒后过滤,你可以改变。
您可以在此链接中找到更多详细信息SwiftUI Combine Debounce TextField
mtb9vblg2#
如果要引入去抖动,只需添加一个
Task.sleep
:如果这样做,您必须将
searchTask
更改为Task<(), Error>?
。然而,去抖动可能不是问题的全部。如果你的模糊过滤太慢,你可能需要让它异步运行,并让它离开主线程:
fuzzyMatch
被标记为async
,但当前没有执行任何异步操作。让方法签名反映正在执行的操作可能会使推理代码变得更容易。1.为此,现在我们意识到
fuzzyMatch
是同步运行的,很明显,如果它运行得慢,它将阻塞当前线程。而且因为Task { ... }
在当前参与者上运行,您最终将阻塞主线程。您应该考虑使用Task.detached
在后台线程上获取它。但是将searchResults
标记为在主参与者上。1.如果您取消
Task
,它不会停止正在进行的fuzzyMatch
。它应该检查取消。所以,把所有这些放在一起,也许:
这并不十分相关,但上面的代码使用了这些扩展: