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

SwiftUI接口参数传递与多列表温度数据重复问题咨询

问题分析与解决方案

你遇到的核心问题是所有行共享同一个全局的温度数据源,导致最后一次接口请求的数据覆盖了之前的结果,所以所有列表行显示完全相同的温度数据。

问题根源

你的AppState里的weatherAveInfos是一个单一数组,每个VegetableInfoRow在出现时都会调用接口更新这个数组。由于接口请求是异步的,后完成的请求会覆盖数组里的旧数据,最终所有行都会显示最后一次请求返回的温度数据——哪怕控制台打印的是不同结果。

解决方案

我提供两种常用修复方案,你可以根据需求选择:


方案一:让每个行独立管理自己的温度数据

这种方案让VegetableInfoRow不再依赖全局的weatherAveInfos,而是自己持有并获取对应温度数据,避免互相干扰。

修改VegetableInfoRow.swift:

import SwiftUI
struct VegetableInfoRow: View {
    var info: DbVegetableInfos
    // 每个row单独存储自己的温度数据
    @State private var weatherAveInfos: [WeatherAveInfos] = []
    
    var body: some View {
        ScrollView(.horizontal) {
            HStack{
                VStack{
                    VStack{
                        Text("start_date:").padding()
                        Text(stringToStringDate(stringDate: info.start_date, format: "yyyy-MM-dd"))
                    }
                }
                Divider()
                    .padding()
                VStack{
                    VStack{
                        Text("progress_temperature:").padding()
                        ForEach(weatherAveInfos){ tempInfo in
                            Text(String(tempInfo.ave_temp))
                        }
                    }
                }
            }
        }
        .onAppear(){
            fetchWeatherAveTemp(start_date: info.start_date)
        }
    }
    
    // 单独封装当前row的温度请求逻辑
    private func fetchWeatherAveTemp(start_date: String) {
        let endpoint: String = "https://sample.com/api/weather_ave/?start_date=\(start_date)"
        guard let url = URL(string: endpoint) else {
            print("Error: cannot create URL")
            return
        }
        var urlRequest = URLRequest(url: url)
        urlRequest.addValue("token xxxxxxxxxx", forHTTPHeaderField: "authorization")
        
        let config = URLSessionConfiguration.default
        let session = URLSession(configuration: config)
        
        let task = session.dataTask(with: urlRequest) { (data, response, error) in
            guard error == nil else {
                print("error calling GET for weather")
                return
            }
            guard let responseData = data else {
                print("Error: did not receive weather data")
                return
            }
            DispatchQueue.main.async {
                do{
                    self.weatherAveInfos = try JSONDecoder().decode([WeatherAveInfos].self, from: responseData)
                    print("temp_info for \(start_date)")
                    for tempInfo in self.weatherAveInfos{
                        print(tempInfo.ave_temp)
                    }
                }catch{
                    print("Error: did not decode weather data")
                    return
                }
            }
        }
        task.resume()
    }
}

func stringToStringDate(stringDate: String, format:String) -> String {
    let formatter: DateFormatter = DateFormatter()
    formatter.calendar = Calendar(identifier: .gregorian)
    formatter.dateFormat = "yyyy/MM/dd"
    guard let newDate = formatter.date(from: stringDate) else {
        return stringDate // 增加容错,避免强制解包崩溃
    }
    formatter.dateFormat = format
    return formatter.string(from: newDate)
}

方案二:用字典存储每个日期对应的温度数据(推荐,可复用数据)

这种方案在AppState里用字典映射每个start_date对应的温度数组,避免重复请求,同时保证每个行能获取到自己对应的数据。

  1. 修改AppState.swift:
import SwiftUI
import Foundation
import Combine
import UIKit
class AppState: ObservableObject {
    @Published var arrayDbVegetableInfos:[DbVegetableInfos]?
    // 用字典存储,key是start_date,value是对应的温度数组
    @Published var weatherAveInfosDict: [String: [WeatherAveInfos]] = [:]
    
    func makeGetCallVegetableInfos() {
        // 原代码保持不变
        let endpoint: String = "https://sample.com/api/info/"
        guard let url = URL(string: endpoint) else {
            print("Error: cannot create URL")
            return
        }
        var urlRequest = URLRequest(url: url)
        urlRequest.addValue("token xxxxxxxxxx", forHTTPHeaderField: "authorization")
        
        let config = URLSessionConfiguration.default
        let session = URLSession(configuration: config)
        
        let task = session.dataTask(with: urlRequest) { (data, response, error) in
            guard error == nil else {
                print("error calling GET")
                print(error!)
                return
            }
            guard let responseData = data else {
                print("Error: did not receive data")
                return
            }
            DispatchQueue.main.async {
                do{
                    self.arrayDbVegetableInfos = try JSONDecoder().decode([DbVegetableInfos].self, from: responseData)
                }catch{
                    print("Error: did not decode")
                    return
                }
            }
        }
        task.resume()
    }
    
    func makeGetCallWeatherAveTemp(start_date:String ) {
        // 先检查字典中是否已有数据,避免重复请求
        if weatherAveInfosDict[start_date] != nil {
            return
        }
        
        let endpoint: String = "https://sample.com/api/weather_ave/?start_date=\(start_date)"
        guard let url = URL(string: endpoint) else {
            print("Error: cannot create URL")
            return
        }
        var urlRequest = URLRequest(url: url)
        urlRequest.addValue("token xxxxxxxxxx", forHTTPHeaderField: "authorization")
        
        let config = URLSessionConfiguration.default
        let session = URLSession(configuration: config)
        
        let task = session.dataTask(with: urlRequest) { (data, response, error) in
            guard error == nil else {
                print("error calling GET")
                return
            }
            guard let responseData = data else {
                print("Error: did not receive data")
                return
            }
            DispatchQueue.main.async {
                do{
                    let weatherData = try JSONDecoder().decode([WeatherAveInfos].self, from: responseData)
                    self.weatherAveInfosDict[start_date] = weatherData
                    print("temp_info for \(start_date)")
                    for info in weatherData{
                        print(info.ave_temp)
                    }
                }catch{
                    print("Error: did not decode")
                    return
                }
            }
        }
        task.resume()
    }
}
  1. 修改VegetableInfoRow.swift:
import SwiftUI
struct VegetableInfoRow: View {
    @EnvironmentObject var appState: AppState
    var info:DbVegetableInfos
    
    // 计算属性:获取当前start_date对应的温度数据
    private var currentWeatherInfos: [WeatherAveInfos] {
        appState.weatherAveInfosDict[info.start_date] ?? []
    }
    
    var body: some View {
        ScrollView(.horizontal) {
            HStack{
                VStack{
                    VStack{
                        Text("start_date:").padding()
                        Text(stringToStringDate(stringDate: info.start_date, format: "yyyy-MM-dd"))
                    }
                }
                Divider()
                    .padding()
                VStack{
                    VStack{
                        Text("progress_temperature:").padding()
                        ForEach(currentWeatherInfos){ tempInfo in
                            Text(String(tempInfo.ave_temp))
                        }
                    }
                }
            }
        }
        .onAppear(){
            appState.makeGetCallWeatherAveTemp(start_date: info.start_date)
        }
    }
}

func stringToStringDate(stringDate: String, format:String) -> String {
    let formatter: DateFormatter = DateFormatter()
    formatter.calendar = Calendar(identifier: .gregorian)
    formatter.dateFormat = "yyyy/MM/dd"
    guard let newDate = formatter.date(from: stringDate) else {
        return stringDate // 增加容错,避免强制解包崩溃
    }
    formatter.dateFormat = format
    return formatter.string(from: newDate)
}

额外优化建议

  • 避免强制解包:我在stringToStringDate里增加了guard let来替代!,防止日期格式错误导致崩溃。
  • 添加加载状态:在温度数据未返回时显示加载指示器,提升用户体验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 19:23:12