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

SwiftUI中TextField无法更新其他类@Published ObservedObject变量

解决SwiftUI TextField无法更新NetworkingManager中@Published变量的问题

嘿,我帮你揪出问题根源啦!你的代码里有两个关键问题,导致输入的城市名没法同步到天气请求里:

问题分析

  • 实例不共享:ContentView和ForecastView各自创建了独立的NetworkingManager实例。你在首页输入的城市名存在第一个实例里,而天气请求用的是第二个全新的实例,它的inputCity自然还是默认的"Sydney"。
  • URL硬编码城市名:就算实例共享了,你的load()方法里还写死了q=Sydney,根本没用到inputCity变量,请求永远只会拉悉尼的天气。

修复方案

1. 共享同一个NetworkingManager实例

我们要把首页的NetworkingManager传递给详情页,让两个页面用同一个实例:

修改ContentView的NavigationLink

NavigationLink(destination: ForecastView(networkingManager: networkingManager)) {
    Text("Go")
}

修改ForecastView的定义

让它接收外部传入的实例,而不是自己初始化:

struct ForecastView: View {
    @ObservedObject var networkingManager: NetworkingManager // 移除原来的实例初始化代码
    // ... 其余代码保持不变
}

2. 修复API请求URL,使用inputCity变量

把load()方法里的硬编码城市名换成inputCity,还要把HTML转义的&改成正常的&,同时加上URL编码处理(避免带空格的城市名出错):

func load() {
    let apiKey = "d81d2f91438e7897ceb3c457c25"
    // 对城市名做URL编码,处理空格等特殊字符
    guard let encodedCity = inputCity.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
          let url = URL(string: "https://api.openweathermap.org/data/2.5/forecast?q=\(encodedCity)&units=metric&APPID=\(apiKey)") else { 
        print("无效的城市名或URL")
        return 
    }
    print("请求的城市是: \(inputCity)")
    URLSession.shared.dataTask(with: url) { (data, response, err) in
        if let err = err {
            print("请求错误: \(err.localizedDescription)")
            return
        }
        guard let data = data else {
            print("未获取到数据")
            return
        }
        do {
            let weatherInfo = try JSONDecoder().decode(WeatherAPIResponse.self, from: data)
            DispatchQueue.main.async {
                self.weatherCity = weatherInfo.city
                self.weatherList = weatherInfo.list
            }
        } catch {
            print("解析错误: \(error.localizedDescription)")
        }
    }.resume()
}

额外优化:把原来的try!换成了do-catch,避免解析失败时直接崩溃,还加了错误打印方便调试。

修改后的完整代码

ContentView.swift

import SwiftUI
struct ContentView: View {
    @ObservedObject var networkingManager = NetworkingManager()
    var body: some View {
        NavigationView {
            VStack {
                Text("Please Enter a City Name")
                TextField("City Name, i.e Sydney", text: $networkingManager.inputCity)
                    .textFieldStyle(.roundedBorder) // 加个样式更美观
                NavigationLink(destination: ForecastView(networkingManager: networkingManager)) {
                    Text("Go")
                        .padding()
                        .background(Color.blue)
                        .foregroundColor(.white)
                        .cornerRadius(8)
                }
            }
            .padding()
            .navigationBarTitle("Weather")
        }
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

NetworkingManager.swift

import Foundation
import Combine
class NetworkingManager : ObservableObject {
    @Published var inputCity = "Sydney"
    @Published var weatherList = [WeatherInfoList]()
    @Published var weatherCity = CityInformation(name: "Sydney", country: "AU")
    
    func load() {
        let apiKey = "d81d2f91438e7897ceb3c457c25"
        guard let encodedCity = inputCity.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
              let url = URL(string: "https://api.openweathermap.org/data/2.5/forecast?q=\(encodedCity)&units=metric&APPID=\(apiKey)") else { 
            print("无效的城市名或URL")
            return 
        }
        print("请求的城市是: \(inputCity)")
        URLSession.shared.dataTask(with: url) { (data, response, err) in
            if let err = err {
                print("请求错误: \(err.localizedDescription)")
                return
            }
            guard let data = data else {
                print("未获取到数据")
                return
            }
            do {
                let weatherInfo = try JSONDecoder().decode(WeatherAPIResponse.self, from: data)
                DispatchQueue.main.async {
                    self.weatherCity = weatherInfo.city
                    self.weatherList = weatherInfo.list
                }
            } catch {
                print("解析错误: \(error.localizedDescription)")
            }
        }.resume()
    }
}
// 补充完整模型定义让代码可运行
struct WeatherAPIResponse: Codable {
    let city: CityInformation
    let list: [WeatherInfoList]
}
struct CityInformation: Codable {
    let name: String
    let country: String
}
struct WeatherInfoList: Codable, Identifiable {
    let id = UUID() // 实现Identifiable,List无需手动指定id
    let dt: Int
    let main: MainWeather
    // 日期格式化计算属性
    var date: String {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd HH:mm"
        return formatter.string(from: Date(timeIntervalSince1970: TimeInterval(dt)))
    }
}
struct MainWeather: Codable {
    let temp: Double // 若原代码是rTemp,可修改为let rTemp: Double
}

ForecastView.swift

import SwiftUI
struct ForecastView: View {
    @ObservedObject var networkingManager: NetworkingManager
    var body: some View {
        NavigationView {
            List(networkingManager.weatherList) { weather in
                HStack {
                    VStack(alignment: .leading) {
                        Text(weather.date)
                            .font(.headline)
                        Text("\(networkingManager.weatherCity.name), \(networkingManager.weatherCity.country)")
                            .font(.subheadline)
                            .foregroundColor(.gray)
                    }
                    Spacer()
                    VStack(alignment: .trailing) {
                        Text(String(format: "%.1f°C", weather.main.temp)) // 格式化温度保留一位小数
                            .font(.title3)
                        Image(systemName: "cloud.sun.fill")
                            .foregroundColor(.yellow)
                    }
                }
            }
            .onAppear() {
                self.networkingManager.load()
            }
            .navigationBarTitle("Weather Forecast")
        }
    }
}
struct ForecastView_Previews: PreviewProvider {
    static var previews: some View {
        ForecastView(networkingManager: NetworkingManager())
    }
}

额外提示

  • 记得把apiKey换成你自己的OpenWeatherMap密钥,否则请求会失败。
  • 可以给TextField添加空输入验证,避免无效请求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:59:28