You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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]

问题原因

  1. 搜索功能始终基于原始的allNorms数据处理,未考虑当前已有的筛选结果;
  2. 取消搜索时直接把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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 11:42:01