SwiftUI实现按钮触发搜索栏及切换机场ICAO标识的问题
SwiftUI内联搜索栏实现方案(替代Sheet)
核心思路是用布尔状态变量控制搜索区域的显示/隐藏,结合NavigationStack实现内联搜索,同时添加实时过滤的搜索结果列表,选中后直接更新按钮标题。
完整实现代码
import SwiftUI // 适配API的Airport模型(根据实际返回结构调整字段) struct Airport: Codable, Identifiable { let id = UUID() let icao: String // 对应ICAO ID,如"KLAX" // 可扩展字段:机场名称、地理位置等 } struct ContentView: View { @State private var homeAirport: String = "KCMA" @State private var searchedAirport: String = "" @State private var isSearchActive = false // 控制搜索区域显隐 @State private var allAirports: [Airport] = [] // 存储待搜索的机场列表 // 实时过滤搜索结果 var filteredAirports: [Airport] { searchedAirport.isEmpty ? allAirports : allAirports.filter { $0.icao.localizedCaseInsensitiveContains(searchedAirport) } } var body: some View { NavigationStack { VStack(alignment: .leading, spacing: 16) { // 机场选择按钮 Button { isSearchActive.toggle() if isSearchActive { searchedAirport = "" } } label: { Text(homeAirport) .font(.largeTitle) .bold() .fontDesign(.monospaced) .foregroundStyle(.green) } // 内联搜索区域 if isSearchActive { VStack(spacing: 12) { TextField("输入ICAO ID搜索", text: $searchedAirport) .textFieldStyle(.roundedBorder) .padding(.horizontal) .focused($isSearchActive) // 激活时自动聚焦输入框 // 搜索结果列表 List(filteredAirports) { airport in Button { homeAirport = airport.icao isSearchActive = false // 选中后隐藏搜索区域 } label: { HStack { Text(airport.icao) .fontDesign(.monospaced) // 可添加机场名称等辅助信息 } } } .listStyle(.plain) .frame(height: 220) // 限制列表高度避免占满屏幕 } } } .padding() .searchable(text: $searchedAirport, prompt: "搜索机场ICAO") .onAppear { // 这里可以提前加载机场列表数据(替换为你的数据源API) // Task { await loadAllAirports() } } } } // 加载单个机场详情(保留原逻辑) func loadAirportDetail() async { guard let url = URL(string: "https://aviationweather.gov/cgi-bin/data/metar.php?ids=\(homeAirport)&hours=0&order=id%2C-obs&sep=true&elev&format=json") else { print("无效URL") return } do { let (data, _) = try await URLSession.shared.data(from: url) let decoded: [Airport] = try JSONDecoder().decode([Airport].self, from: data) // 处理返回的机场数据 } catch { print(error) } } // 加载全量机场列表用于搜索过滤 func loadAllAirports() async { guard let url = URL(string: "你的全机场数据API地址") else { return } do { let (data, _) = try await URLSession.shared.data(from: url) allAirports = try JSONDecoder().decode([Airport].self, from: data) } catch { print(error) } } }
关键实现说明
- 内联搜索控制:用
isSearchActive变量替代Sheet,实现搜索区域的切换显示,避免弹出新页面。 - 搜索体验优化:通过
focused($isSearchActive)在显示搜索区域时自动聚焦输入框,减少用户操作步骤。 - 实时过滤逻辑:通过计算属性
filteredAirports根据输入内容实时筛选机场,无需额外触发请求。 - 选中反馈:点击搜索结果后直接更新按钮标题并隐藏搜索区域,完成闭环操作。
- 修正
NavigationStack用法:将.searchable修饰符放在导航栈内部的视图上,确保搜索功能正常触发(原代码中.task嵌套ContentView的写法错误)。
注意事项
- 若没有全量机场API,可调整为根据输入实时请求单机场数据,但无法实现多结果过滤,建议提前缓存常用机场列表。
- 可根据需求自定义搜索区域的样式、列表高度等UI细节。
内容的提问来源于stack exchange,提问作者Yuda
相关产品推荐
相关产品推荐

