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
相关产品推荐
相关产品推荐

