查询数组过大耗时,如何让UISearchBar展示完整输入文本的搜索结果
问题根因
你的代码存在两个核心问题导致旧搜索结果覆盖新结果:
- 你写的校验逻辑
searchText == searchTextBeingQuireid完全无效:这两个值是你在同一个闭包作用域里赋值的相同值,判断永远为真,无法过滤掉已经过时的旧搜索任务 - 连续输入时会生成多个并发的后台搜索任务,耗时更长的旧任务(比如首字母搜索)结束后依然会执行UI刷新,覆盖新搜索的结果
修复方案
推荐使用可取消的DispatchWorkItem管理搜索任务,从根源上避免旧任务干扰,还可以搭配防抖逻辑提升性能:
- 首先给当前控制器新增属性,存储当前活跃的搜索任务:
private var currentSearchWorkItem: DispatchWorkItem?
- 替换
searchBar的textDidChange实现:
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { // 取消所有旧的未完成搜索任务 currentSearchWorkItem?.cancel() let workItem = DispatchWorkItem { [weak self] in guard let self = self, !workItem.isCancelled else { return } var searchedRecords = [AlimentObject]() let words = Set(searchText.split(separator: " ").map(String.init)) if searchText.isEmpty { searchedRecords = [] } else { // 前缀匹配结果 var classicFilter = baseOuChercheAliments.filter { $0.nomAliment.range(of: searchText, options: .anchored) != nil } classicFilter.sort { $0.nomAliment < $1.nomAliment } // 全词包含匹配结果 var complexFilter = baseOuChercheAliments.filter { object in guard !workItem.isCancelled else { return false } return words.allSatisfy { word in object.nomAliment.localizedCaseInsensitiveContains(word) } } complexFilter.sort { $0.nomAliment < $1.nomAliment } // 结果去重合并 let subtraction = Array(Set(complexFilter).subtracting(classicFilter)) .sorted { $0.nomAliment > $1.nomAliment } searchedRecords = classicFilter + subtraction } // 仅未取消的最新任务可以刷新UI guard !workItem.isCancelled else { return } DispatchQueue.main.async { self.baseDeDonneesAlimentsFiltree = searchedRecords self.tableView.reloadData() } } currentSearchWorkItem = workItem // 可选:加300ms防抖,用户停止输入后再执行搜索,减少无效计算,不需要可直接去掉延迟参数 DispatchQueue.global(qos: .default).asyncAfter(deadline: .now() + 0.3, execute: workItem) }
轻量化替代方案
如果你不需要取消旧任务,只想用最低成本修复,也可以通过全局最新搜索关键词校验实现:
- 新增控制器属性:
private var latestSearchKeyword: String = ""
- 修改搜索逻辑:
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { // 先更新最新搜索关键词 latestSearchKeyword = searchText DispatchQueue.global(qos: .default).async { [self] in // 原有搜索逻辑不变... // 刷新UI前校验当前搜索是否是最新的 guard searchText == latestSearchKeyword else { return } DispatchQueue.main.async { baseDeDonneesAlimentsFiltree = searchedRecords self.tableView.reloadData() } } }
内容的提问来源于stack exchange,提问作者Maria23
相关产品推荐
相关产品推荐

