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

SwiftUI中实现CSV文件数据的可搜索列表问题求助

SwiftUI 机场列表搜索功能实现方案

核心修改思路

把搜索过滤逻辑放在ViewModel中统一维护,利用@Published属性自动触发View刷新,同时优化CSV读取的冗余全局变量问题。

1. 修改AirportsViewModel,添加搜索过滤逻辑

class AirportsViewModel: NSObject, ObservableObject {
    @Published var allAirports = [Airports2]() // 存储所有原始机场数据
    @Published var searchText = "" // 将搜索文本移至ViewModel统一管理
    
    // 计算属性:根据搜索文本动态过滤机场列表
    var filteredAirports: [Airports2] {
        guard !searchText.isEmpty else {
            return allAirports
        }
        let lowercasedSearch = searchText.lowercased()
        return allAirports.filter { airport in
            // 可根据需求添加更多搜索字段
            airport.Icao.lowercased().contains(lowercasedSearch) ||
            airport.Name.lowercased().contains(lowercasedSearch) ||
            airport.Iata.lowercased().contains(lowercasedSearch)
        }
    }
    
    override init() {
        super.init()
        allAirports = AirportsClass.retrieveAP()
    }
}

2. 优化AirportsClass,移除全局变量避免重复数据

class AirportsClass {
    static let bundleURL =  Bundle.main.url(forResource: "FR_Airports", withExtension: "csv")!
    
    static func retrieveAP() -> [Airports2] {
        guard let data = try? Data(contentsOf: bundleURL) else {
            fatalError("Unable to load airports data")
        }
        guard let decoder = String(data: data, encoding: .utf8) else {
            fatalError("Failed to decode data to string")
        }
        let dataArr = decoder.components(separatedBy: "\n").map { $0.components(separatedBy: ",") }
        var airports = [Airports2]() // 使用局部数组存储结果,避免全局变量污染
        
        // dropFirst()用于跳过CSV表头(如果你的CSV第一行是字段名,无表头可移除该方法)
        for line in dataArr.dropFirst() {
            guard line.count >= 18 else { continue } // 防止数据不完整导致数组越界
            let item = Airports2(
                AirportID: line[0],
                Icao: line[1],
                ApType: line[2],
                Name: line[3],
                Latitude: line[4],
                Longitude: line[5],
                Altitude: line[6],
                Continent: line[7],
                Country: line[8],
                Region: line[9],
                Municipality: line[10],
                Service: line[11],
                GpsCode: line[12],
                Iata: line[13],
                LocalCode: line[14],
                Link: line[15],
                wikiLink: line[16],
                Keyword: line[17]
            )
            airports.append(item)
        }
        return airports
    }
}

3. 修改AirportView,绑定搜索文本并展示过滤后的数据

struct AirportView: View {
    @StateObject var airportsVM = AirportsViewModel() // 使用StateObject确保ViewModel生命周期与View同步
    
    var body: some View {
        NavigationView { 
            List{
                Section(header: Text("Suisse")){
                    Text("Hello World")
                }
                Section(header: Text("France")) {
                    ForEach(airportsVM.filteredAirports) { airport in
                        NavigationLink(destination: Text(airport.ApType)) {
                            Text("\(airport.Icao) - \(airport.Name)")
                        }
                    }
                }
            }
        }
        .navigationTitle("Airports")
        .searchable(text: $airportsVM.searchText) // 绑定ViewModel的搜索文本
        .navigationViewStyle(StackNavigationViewStyle())
    }
}

关键说明

  • MVVM架构适配:将数据逻辑与视图分离,View只负责展示,ViewModel处理数据过滤和状态管理。
  • 自动刷新机制:filteredAirports作为计算属性,会在searchText或allAirports变化时自动重计算,触发View更新。
  • 崩溃防护:添加数组长度判断,避免CSV行数据不完整导致的越界崩溃;跳过表头逻辑适配常见CSV格式。
  • 生命周期优化:使用@StateObject初始化ViewModel,确保在View生命周期内仅实例化一次。

内容的提问来源于stack exchange,提问作者flyer74

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 08:12:27