SwiftUI中如何通过TextField修改WeatherClass的city变量
解决方案
核心问题分析
你的代码存在两个关键问题:
WeatherClass的city变量未标记为@Published,无法被SwiftUI观察到变化;cityTextField的@State是局部状态,和WeatherClass的city没有任何关联,输入内容无法同步到ViewModel。
修改后的完整代码
1. 更新WeatherClass
class WeatherClass: ObservableObject { @Published var weatherAddress: String = "" @Published var weatherDays: [WeatherDays] = [] @Published var city: String = "" // 改为@Published,让SwiftUI能观察到变化 func fetch() { guard !city.isEmpty else { return } // 空城市名不发起请求 let location = city let apiKey = "AP8LUYMSTHZ" // 安全创建URL,避免强制解包崩溃 guard let url = URL(string: "https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/\(location)?key=\(apiKey)") else { print("无效的城市URL") return } URLSession.shared.dataTask(with: url) { data, response, error in guard let data = data else { print("未获取到数据") return } // 用do-catch捕获解析错误,方便调试 do { let weather = try JSONDecoder().decode(WeatherData.self, from: data) DispatchQueue.main.async { self.weatherAddress = weather.resolvedAddress self.weatherDays = weather.days } } catch { print("JSON解析失败: \(error.localizedDescription)") } }.resume() } } struct WeatherData: Decodable { let resolvedAddress: String let days: [WeatherDays] } struct WeatherDays: Hashable, Decodable { let datetime: String let tempmax: Double let tempmin: Double let description: String }
2. 重构CityTextField视图
struct CityTextField: View { // 遵循Swift命名规范,首字母大写 @ObservedObject var weatherVM: WeatherClass var onSearch: () -> Void // 搜索触发回调 var body: some View { HStack { TextField("搜索城市", text: $weatherVM.city) .frame(height:30) .multilineTextAlignment(.center) .background(Color.gray.opacity(0.2)) .cornerRadius(25) .padding(.horizontal) .onSubmit { // 回车时触发搜索 onSearch() } // 新增搜索按钮,手动触发请求 Button(action: onSearch) { Image(systemName: "magnifyingglass") .foregroundColor(.blue) } .padding(.trailing) } } }
3. 主视图使用示例
struct ContentView: View { // iOS 14+用@StateObject创建ObservableObject实例 @StateObject private var weatherVM = WeatherClass() var body: some View { VStack(spacing: 20) { CityTextField(weatherVM: weatherVM) { weatherVM.fetch() } // 显示解析后的城市地址 if !weatherVM.weatherAddress.isEmpty { Text("当前城市: \(weatherVM.weatherAddress)") .font(.headline) } // 显示天气列表 List(weatherVM.weatherDays, id: \.self) { day in VStack(alignment: .leading, spacing: 4) { Text(day.datetime) .font(.subheadline) .foregroundColor(.gray) Text("最高温: \(day.tempmax)° / 最低温: \(day.tempmin)°") Text(day.description) .font(.caption) } .padding(.vertical, 8) } } .padding(.top) } }
关键修改说明
@Published标记city:让WeatherClass的city变量变化时,自动通知所有绑定的视图更新;- 视图与ViewModel绑定:
CityTextField通过@ObservedObject接收WeatherClass实例,直接将TextField的文本绑定到weatherVM.city,实现输入内容同步; - 安全处理URL和解析:替换强制解包为可选绑定,用
do-catch捕获解析错误,避免崩溃并方便调试; - 触发搜索的方式:同时支持回车触发和按钮触发,提升用户体验;
- 遵循Swift命名规范:结构体/类名首字母大写,代码更易读。
内容的提问来源于stack exchange,提问作者SirEdwardIII
相关产品推荐
相关产品推荐

