SwiftUI列表搜索时如何保留筛选设置
SwiftUI List筛选与搜索功能问题排查与修复
问题现象
- 筛选出仅包含“Halkalı”的结果后,进行搜索会出现不符合筛选条件的内容(如“EN 10305-5”和“DIN Norm Sample”);
- 取消搜索后,列表回到未筛选的全部结果状态。
相关代码
主页面代码
// Created by Caner Altuner on 31.07.2023. // import SwiftUI struct NormCatalogView: View { //To access to the model files @State private var normsPlant: [NormModel] = allNorms @State private var normsSearch: [NormModel] = allNorms //The variable for search @State private var searchText: String = "" @State private var filterPlant: String = "" //Boolean for the alert @State private var showAlert = false var body: some View { NavigationView { VStack { List { //ForEach ile bu işlemi daha basit bir yolla structlar içinde yer alan elemanları liste içine getirebiliyoruz ForEach(normsSearch) {norm in //Normları bölümlere göre ayırlmak için Section elementini kullanıyoruz Section(header: Text(norm.title)) { ForEach(norm.elements) { element in //Sonraki sayfaya geçişi sağlayan bölüm NavigationLink(destination: NormDetailsView(chosenNormElement: element)) { //İsimleri listeye aktardığımız yer Text(element.name) } } } } //En üstteki başlığın belirlenmesi ve stilinin ayarlanması }.toolbar(content: { ToolbarItem(placement: .navigationBarTrailing) { Button("Filter") { showAlert = true }.alert("Filter by Production Plant", isPresented: $showAlert) { Button("Filter as All Production Plants", role: .cancel) { filterPlant = "All" filterByPlant(filterPlant) } Button("Filter as Halkalı") { filterPlant = "Halkalı" filterByPlant(filterPlant) } Button("Filter as Gemlik") { filterPlant = "Gemlik" filterByPlant(filterPlant) } Button("Filter as Vobarno") { filterPlant = "Vobarno" filterByPlant(filterPlant) } Button("Filter as Houston") { filterPlant = "Houston" filterByPlant(filterPlant) } } message: { Text("How would you like to filter your search?") } } }).navigationBarTitle("Borkatalog").navigationBarTitleDisplayMode(.inline) .searchable(text: $searchText, prompt: "Search in Norms").onChange(of: searchText) { search in //Aramanın gerçekleştiği bölüm searchNorms(search) } } } } //Search Function func searchNorms(_ searchText: String) { if searchText == "" { normsSearch = allNorms return } var temp = allNorms for i in temp.indices { temp[i].elements = temp[i].elements.filter { $0.name.localizedStandardContains(searchText) } } normsSearch = temp.filter { !$0.elements.isEmpty } } //Filter by Plant func filterByPlant(_ filterPlant: String) { if filterPlant == "All" { normsPlant = allNorms return } var temp = allNorms for i in temp.indices { temp[i].elements = temp[i].elements.filter { $0.productionPlant.localizedStandardContains(filterPlant) } } normsPlant = temp.filter { !$0.elements.isEmpty } print(normsPlant) } } struct NormCatalogView_Previews: PreviewProvider { static var previews: some View { NormCatalogView() } }
Norms.swift(结构体文件)
struct NormModel : Identifiable { var id = UUID() var title : String var elements : [NormElements] } struct NormElements : Identifiable { var id = UUID() var name : String var productionPlant : String } let en103052 = NormElements(name: "EN 10305-2", productionPlant: "Halkalı") let en103053 = NormElements(name: "EN 10305-3", productionPlant: "Halkalı, Gemlik") let en103055 = NormElements(name: "EN 10305-5", productionPlant: "Gemlik, Vobarno") let ornekDinNorm = NormElements(name: "DIN Norm Sample", productionPlant: "Vobarno") let enNorms = NormModel(title: "EN Normları", elements:[en103052, en103053, en103055]) let dinNorms = NormModel(title: "DIN Normları", elements: [ornekDinNorm]) let allNorms = [enNorms, dinNorms]
问题原因
- 搜索功能始终基于原始的
allNorms数据处理,未考虑当前已有的筛选结果; - 取消搜索时直接把
normsSearch重置为allNorms,丢失了之前的筛选状态。
修复方案
修改后的主页面代码
// Created by Caner Altuner on 31.07.2023. // import SwiftUI struct NormCatalogView: View { // 原始数据源 private let originalNorms: [NormModel] = allNorms // 仅筛选后的结果 @State private var filteredNorms: [NormModel] = allNorms // 最终展示的筛选+搜索结果 @State private var displayedNorms: [NormModel] = allNorms @State private var searchText: String = "" @State private var filterPlant: String = "All" @State private var showAlert = false var body: some View { NavigationView { VStack { List { ForEach(displayedNorms) { norm in Section(header: Text(norm.title)) { ForEach(norm.elements) { element in NavigationLink(destination: NormDetailsView(chosenNormElement: element)) { Text(element.name) } } } } } .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button("Filter") { showAlert = true } .alert("Filter by Production Plant", isPresented: $showAlert) { Button("Filter as All Production Plants", role: .cancel) { filterPlant = "All" updateFilteredNorms() } Button("Filter as Halkalı") { filterPlant = "Halkalı" updateFilteredNorms() } Button("Filter as Gemlik") { filterPlant = "Gemlik" updateFilteredNorms() } Button("Filter as Vobarno") { filterPlant = "Vobarno" updateFilteredNorms() } Button("Filter as Houston") { filterPlant = "Houston" updateFilteredNorms() } } message: { Text("How would you like to filter your search?") } } } .navigationBarTitle("Borkatalog") .navigationBarTitleDisplayMode(.inline) .searchable(text: $searchText, prompt: "Search in Norms") .onChange(of: searchText) { _ in updateDisplayedNorms() } .onChange(of: filteredNorms) { _ in updateDisplayedNorms() } } } } // 更新筛选后的数据源 private func updateFilteredNorms() { if filterPlant == "All" { filteredNorms = originalNorms return } var temp = originalNorms for i in temp.indices { temp[i].elements = temp[i].elements.filter { $0.productionPlant.localizedStandardContains(filterPlant) } } filteredNorms = temp.filter { !$0.elements.isEmpty } } // 根据搜索词和筛选结果更新最终展示数据 private func updateDisplayedNorms() { guard !searchText.isEmpty else { displayedNorms = filteredNorms return } var temp = filteredNorms for i in temp.indices { temp[i].elements = temp[i].elements.filter { $0.name.localizedStandardContains(searchText) } } displayedNorms = temp.filter { !$0.elements.isEmpty } } } struct NormCatalogView_Previews: PreviewProvider { static var previews: some View { NormCatalogView() } }
修复说明
- 拆分数据源:
originalNorms(原始数据)、filteredNorms(仅筛选后的结果)、displayedNorms(筛选+搜索后的最终展示数据); - 筛选操作更新
filteredNorms,搜索操作基于filteredNorms处理,保证筛选与搜索的叠加效果; - 取消搜索时,
displayedNorms直接恢复为当前的filteredNorms,保留筛选状态; - 通过
onChange监听filteredNorms变化,确保筛选后自动更新搜索结果。
内容的提问来源于stack exchange,提问作者caneraltuner
相关产品推荐
相关产品推荐

