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

如何延迟SwiftUI视图渲染直至@Published对象加载完成

问题核心原因
  • SwiftUI视图在onAppear执行前就会完成首次body渲染,此时weatherResponse作为可选值默认是nil,你代码里直接用!强制解包,会立刻触发空值崩溃,根本等不到异步网络请求返回。
  • 你的代码里还有一处关键错误:@StateObject是仅能在SwiftUI View结构体中使用的属性包装器,在ObservableObject类里用@StateObject持有LocationManager会导致生命周期管理失效,这也是getMainWeather()内打印语句不执行的核心诱因。
修复方案
  • 修正属性包装器误用:将WeatherNetworking类中的@StateObject var locationManager = LocationManager()替换为@ObservedObject var locationManager = LocationManager()。
  • 移除所有强制解包逻辑:针对可选类型的weatherResponse做分支渲染,数据未返回时显示加载态,数据返回后再渲染天气内容,从根源避免空值访问。
  • (可选优化)如果需要等定位权限申请完成、拿到有效经纬度再发请求,可以在WeatherNetworking中监听定位状态变化,自动触发天气请求,避免用默认经纬度(0,0)发起无效请求。

修正后的视图代码

struct MainWeatherView: View {
    
    @StateObject var weatherNetworking = WeatherNetworking()

    var body: some View {
        VStack {
            // 先解包weatherResponse,非空再渲染实际内容
            if let weatherData = weatherNetworking.weatherResponse {
                let currentCondition = weatherData.current.weather[0]
                Image(systemName: weatherNetworking.getConditionName(weatherID: currentCondition.id))
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .scaleEffect(0.75)
                    .padding()
                
                HStack {
                    VStack{
                        Text("Temperature")
                            .fontWeight(.bold)
                            .font(.system(size: 24))
                        Text("Humidity")
                            .fontWeight(.bold)
                            .font(.system(size: 24))
                            // 其余原有文本字段
                    }
                    
                    VStack {
                        Text("\(weatherData.current.temp, specifier: "%.2f") °F")
                            .fontWeight(.bold)
                            .font(.system(size: 24))
                        Text("\(weatherData.current.humidity, specifier: "%.0f") %")
                            .fontWeight(.bold)
                            .font(.system(size: 24))
                            // 其余原有文本字段
                    }
                }
            } else {
                // 数据加载中状态,可根据需求替换为骨架屏、占位图
                ProgressView("天气数据加载中")
            }

            // 如果有加载错误可以在这里加错误提示
            if let error = weatherNetworking.loadError {
                Text("加载失败:\(error.localizedDescription)")
                    .foregroundColor(.red)
            }
        }
        .onAppear {
            self.weatherNetworking.getMainWeather()
        }
    }
}

修正后的网络类代码

class WeatherNetworking: ObservableObject {
    
    // 类中持有ObservableObject用@ObservedObject,不要用@StateObject
    @ObservedObject var locationManager = LocationManager()
    @Published var weatherResponse: WeatherResponse?
    @Published var loadError: Error?
    
    func getMainWeather() {
        let requestURL = URL(string: "https://api.openweathermap.org/data/2.5/onecall?appid=redacted&exclude=minutely&units=imperial&lat=\(locationManager.lastLocation?.coordinate.latitude ?? 0)&lon=\(locationManager.lastLocation?.coordinate.longitude ?? 0)")
        
        guard let url = requestURL else { return }
        
        URLSession.shared.dataTask(with: url) { [weak self] data, response, error in
            guard let self = self else { return }
            // 回到主线程更新UI相关属性
            DispatchQueue.main.async {
                if let error = error {
                    self.loadError = error
                    return
                }
                guard let validData = data else {
                    self.loadError = NSError(domain: "WeatherError", code: -1, userInfo: [NSLocalizedDescriptionKey: "无有效返回数据"])
                    return
                }
                do {
                    let decodedResult = try JSONDecoder().decode(WeatherResponse.self, from: validData)
                    self.weatherResponse = decodedResult
                } catch {
                    self.loadError = error
                }
            }
        }.resume()
    } 
}

提示:如果你的定位是异步获取的,不要在onAppear里直接调用请求,可以给LocationManager的lastLocation加didSet监听,或者在WeatherNetworking初始化时用Combine订阅locationManager.$lastLocation,拿到非空的有效定位后再触发网络请求,避免请求参数错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 20:33:19