关于在用户查询后将数据从 API 传递到视图控制器的问题 - Swift

Posted

技术标签:

【中文标题】关于在用户查询后将数据从 API 传递到视图控制器的问题 - Swift【英文标题】:Question about passing data from an API into a view controller after user query - Swift 【发布时间】:2020-09-22 21:46:24 【问题描述】:

我正在不知疲倦地开发一个应用程序,而且我对 Swift 还是很陌生。我试图在用户在登陆屏幕上输入后从 API 调用中获取数据,然后在出现的模式弹出窗口上显示 API 调用的结果,以显示用户想要的数据。这是一个航空天气应用程序;用户输入类似“KDAB”的内容,API 返回该机场当前的航空天气。我为不同的值设置了一堆标签,可以在搜索后将它们打印到控制台,但似乎无法在 VC 中打印它们。我尝试了一些destination.segue 修复,但不明白我为什么要使用它们——数据直接来自解码的JSON。帮助将不胜感激!

我试图在每个值标签的 ReportViewController 上显示。 IE。通过使用 .text 属性在 windSpeedValueLabel 中的风速。

现在,实际 ReportViewController 上的默认标签没有任何变化。

再次感谢。

我的代码:

//
//  ReportViewController.swift
//  AvWx Pro
//
//  Created by Grayson Bertaina on 9/22/20.
//

import UIKit

class ReportViewController: UIViewController, WeatherManagerDelegate 

    var weatherManager = WeatherManager()
    
    @IBOutlet weak var flightRulesTitleLabel: UILabel!
    
    @IBOutlet weak var flightRulesValueLabel: UILabel!
    
    @IBOutlet weak var visibilityValueLabel: UILabel!
    
    @IBOutlet weak var altimeterValueLabel: UILabel!
    
    @IBOutlet weak var cloudsTitleLabel: UILabel!
    
    @IBOutlet weak var cloudsType1Label: UILabel!
    
    @IBOutlet weak var cloudsAltitude1Label: UILabel!
    
    @IBOutlet weak var cloudsType2Label: UILabel!
    
    @IBOutlet weak var cloudsAltitude2Label: UILabel!
    
    @IBOutlet weak var cloudsType3Label: UILabel!
    
    @IBOutlet weak var cloudsAltitude3Label: UILabel!
    
    @IBOutlet weak var windGTextLabel: UILabel!
    
    @IBOutlet weak var windSpeedValueLabel: UILabel!
    
    @IBOutlet weak var windGustValueLabel: UILabel!
    
    @IBOutlet weak var windFromTextLabel: UILabel!
    
    @IBOutlet weak var windDirectionValueLabel: UILabel!
    
    @IBOutlet weak var remarksValueLabel: UILabel!
    
    override func viewDidLoad() 
        super.viewDidLoad()
        
        // Do any additional setup after loading the view.
    
    

    func didUpdateWeather(_ weatherManager: WeatherManager, weather: WeatherModel) 
        DispatchQueue.main.async 
            self.flightRulesValueLabel.text = weather.flightRules
            self.cloudsType1Label.text = weather.lowestCloudsType
        
        
    
    
    func didFailWithError(error: Error) 
        print(error)
    
    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) 
        // Get the new view controller using segue.destination.
        // Pass the selected object to the new view controller.
    
    


//
//  ViewController.swift
//  AvWx Pro
//
//  Created by Grayson Bertaina on 9/21/20.
//

import UIKit

class WxViewController: UIViewController, UITextFieldDelegate, WeatherManagerDelegate 

    var weatherManager = WeatherManager()
    
    @IBOutlet weak var stationSearch: UITextField!
    
    
    override func viewDidLoad() 
        super.viewDidLoad()
        
        weatherManager.delegate = self
        stationSearch.delegate = self
    

    @IBAction func searchPressed(_ sender: Any) 
        print(stationSearch.text!)
        stationSearch.endEditing(true)
    
    
    func textFieldShouldReturn(_ textField: UITextField) -> Bool 
        print(stationSearch.text!)
        stationSearch.endEditing(true)
        return true
    
    
    func textFieldDidEndEditing(_ textField: UITextField) 
        if let station = stationSearch.text 
            weatherManager.fetchWeather(stationICAO: station)
        

        stationSearch.text = ""
    
    
    func textFieldShouldEndEditing(_ textField: UITextField) -> Bool 
        if stationSearch.text != "" 
            return true
         else 
            stationSearch.placeholder = "Type an ICAO"
            return false
        
    
    
    func didUpdateWeather(_ weatherManager: WeatherManager, weather: WeatherModel) 
        print(weather.flightConditions)
        
        
    
    
    func didFailWithError(error: Error) 
        print(error)
    
    
    
  
    
    

//
//  WeatherManager.swift
//  AvWx Pro
//
//  Created by Grayson Bertaina on 9/21/20.
//

import Foundation

protocol WeatherManagerDelegate 
    func didUpdateWeather(_ weatherManager: WeatherManager, weather: WeatherModel)
    func didFailWithError(error: Error)


struct WeatherManager 
    let weatherURL = "https://avwx.rest/api/metar/"
    
    var delegate : WeatherManagerDelegate?

    func fetchWeather (stationICAO: String) 
        let urlString = "\(weatherURL)\(stationICAO)?token=OVi45FiTDo1LmyodShfOfoizNe5m9wyuO6Mkc95AN-c"
        performRequest(with: urlString)
    
    
    func performRequest (with urlString: String) 
        if let url = URL(string: urlString) 
            let session = URLSession(configuration: .default)
                
            
            let task = session.dataTask(with: url)  (data, response, error) in
                if error != nil 
                    self.delegate?.didFailWithError(error: error!)
                    return
                
                
                if let safeData = data 
                    if let weather = self.parseJSON(safeData) 
                        self.delegate?.didUpdateWeather(self, weather: weather)
                    
                
            
            
            task.resume()
            print(urlString)
            
            
            
        
    
    func parseJSON(_ weatherData: Data) -> WeatherModel? 
        
        
        do 
            let decoder = JSONDecoder()
            let decodedData = try decoder.decode(WeatherData.self, from: weatherData)
            
            
            
            let clouds = decodedData.clouds
            let lowCloudsType = (clouds.count > 0 ? clouds[0]?.type : nil) ?? "N/A"
            let midCloudsType = (clouds.count > 1 ? clouds[1]?.type : nil) ?? "N/A"
            let highCloudsType = (clouds.count > 2 ? clouds[2]?.type : nil) ?? "N/A"
            let lowCloudsAlt = (clouds.count > 0 ? clouds[0]?.altitude : nil) ?? 0
            let midCloudsAlt = (clouds.count > 1 ? clouds[1]?.altitude : nil) ?? 0
            let highCloudsAlt = (clouds.count > 2 ? clouds[2]?.altitude : nil) ?? 0
            let reportingStationVar = decodedData.station ?? "N/A"
            let windGustValue = decodedData.wind_gust?.value ?? 0
            let windSpeedValue = decodedData.wind_speed?.value ?? 0
            let windDirectionValue = decodedData.wind_direction?.value ?? 999
            let visibilityValue = decodedData.visibility?.value ?? 0
            let flightRulesValue = decodedData.flight_rules ?? "N/A"
            
            let weather = WeatherModel(lowestCloudsType: lowCloudsType , lowestCloudsAlt: lowCloudsAlt, middleCloudsType: midCloudsType , middleCloudsAlt: midCloudsAlt, highestCloudsType: highCloudsType , highestCloudsAlt: highCloudsAlt, reportingStation: reportingStationVar, windGust: windGustValue, windSpeed: windSpeedValue, windDirection: windDirectionValue, visibility: visibilityValue, flightRules: flightRulesValue)
            
            return weather
            
         catch 
            delegate?.didFailWithError(error: error)
            return nil
        
    
    
    


//
//  WeatherData.swift
//  AvWx Pro
//
//  Created by Grayson Bertaina on 9/21/20.
//

import Foundation

struct WeatherData: Codable 
   
    
    let clouds: [Clouds?]
    let flight_rules: String?
    let remarks: String?
    let wind_speed: WindSpeed?
    let wind_gust: WindGust?
    let wind_direction: WindDirection?
    let visibility: Visibility?

    let station: String?




struct Clouds: Codable 
    let type: String
    let altitude: Int


struct WindSpeed: Codable 
    let value: Int


struct WindGust: Codable 
    let value: Int


struct WindDirection: Codable 
    let value: Int


struct Visibility: Codable 
    let value: Int



再次感谢。

【问题讨论】:

您在 WxViewController 中的代码做了正确的事情:它设置委托并在 weatherManager 上调用 fetchWeather。但是这些都不是在您的 ReportViewController 中完成的 【参考方案1】:

首先,在 WeatherManager 上,将您的委托声明为弱以避免引用循环

weak var delegate : WeatherManagerDelegate?

其次,解码数据后,您必须调用委托并传递数据,在这种情况下,您刚刚使用委托方法之一创建的 WeatherModel

 do 
            let decoder = JSONDecoder()
            let decodedData = try decoder.decode(WeatherData.self, from: weatherData)
            
            
            
            let clouds = decodedData.clouds
            let lowCloudsType = (clouds.count > 0 ? clouds[0]?.type : nil) ?? "N/A"
            let midCloudsType = (clouds.count > 1 ? clouds[1]?.type : nil) ?? "N/A"
            let highCloudsType = (clouds.count > 2 ? clouds[2]?.type : nil) ?? "N/A"
            let lowCloudsAlt = (clouds.count > 0 ? clouds[0]?.altitude : nil) ?? 0
            let midCloudsAlt = (clouds.count > 1 ? clouds[1]?.altitude : nil) ?? 0
            let highCloudsAlt = (clouds.count > 2 ? clouds[2]?.altitude : nil) ?? 0
            let reportingStationVar = decodedData.station ?? "N/A"
            let windGustValue = decodedData.wind_gust?.value ?? 0
            let windSpeedValue = decodedData.wind_speed?.value ?? 0
            let windDirectionValue = decodedData.wind_direction?.value ?? 999
            let visibilityValue = decodedData.visibility?.value ?? 0
            let flightRulesValue = decodedData.flight_rules ?? "N/A"
            
            let weather = WeatherModel(lowestCloudsType: lowCloudsType , lowestCloudsAlt: lowCloudsAlt, middleCloudsType: midCloudsType , middleCloudsAlt: midCloudsAlt, highestCloudsType: highCloudsType , highestCloudsAlt: highCloudsAlt, reportingStation: reportingStationVar, windGust: windGustValue, windSpeed: windSpeedValue, windDirection: windDirectionValue, visibility: visibilityValue, flightRules: flightRulesValue)
            
            delegate?.didUpdateWeather(self, weather: weather)

            return weather
            
         catch 
            delegate?.didFailWithError(error: error)
            return nil
        

在 ReportViewController 上,将 self 分配为 viewDidLoad() 上的 WeatherManagerDelegate,然后您必须通过使用相应值更新您的标签来实现委托函数 didUpdateWeather - 您已经开始了

class ReportViewController: UIViewController, WeatherManagerDelegate 

    var weatherManager = WeatherManager()
    
    @IBOutlet weak var flightRulesTitleLabel: UILabel!
    
    @IBOutlet weak var flightRulesValueLabel: UILabel!
    
    @IBOutlet weak var visibilityValueLabel: UILabel!
    
    @IBOutlet weak var altimeterValueLabel: UILabel!
    
    @IBOutlet weak var cloudsTitleLabel: UILabel!
    
    @IBOutlet weak var cloudsType1Label: UILabel!
    
    @IBOutlet weak var cloudsAltitude1Label: UILabel!
    
    @IBOutlet weak var cloudsType2Label: UILabel!
    
    @IBOutlet weak var cloudsAltitude2Label: UILabel!
    
    @IBOutlet weak var cloudsType3Label: UILabel!
    
    @IBOutlet weak var cloudsAltitude3Label: UILabel!
    
    @IBOutlet weak var windGTextLabel: UILabel!
    
    @IBOutlet weak var windSpeedValueLabel: UILabel!
    
    @IBOutlet weak var windGustValueLabel: UILabel!
    
    @IBOutlet weak var windFromTextLabel: UILabel!
    
    @IBOutlet weak var windDirectionValueLabel: UILabel!
    
    @IBOutlet weak var remarksValueLabel: UILabel!
    
    override func viewDidLoad() 
        super.viewDidLoad()
        
        weatherManager.delegate = self
    
    

    func didUpdateWeather(_ weatherManager: WeatherManager, weather: WeatherModel) 
        DispatchQueue.main.async 
            self.flightRulesValueLabel.text = weather.flightRules
            self.cloudsType1Label.text = weather.lowestCloudsType
        
        
 

【讨论】:

您好,非常感谢您的回答。我尝试了您的修复 - 没有错误,但屏幕仍然没有更新。有什么想法吗? 我建议在委托函数调用“didUpdateWeather”内的 ReportViewController 行上放置一个断点。获取数据后看是否命中断点。另外,我注意到你有另一个 ViewController 叫做 WxViewController,它也有相同的委托函数,也可以在上面设置一个断点。我猜你有错误的视图控制器,而不是带有标签的 ReportViewController。此外,通过为 viewDidLoad 上的文本分配一个值来测试您的标签连接,以确保您在屏幕上拥有正确的视图。

以上是关于关于在用户查询后将数据从 API 传递到视图控制器的问题 - Swift的主要内容,如果未能解决你的问题,请参考以下文章

在 Laravel 5.1 中通过 AJAX 将用户输入数据从视图传递到控制器

如何在asp.net mvc中将数据从视图传递到控制器

将数据从 viewcontroller tableview 传递到另一个视图控制器

单击按钮后将名称附加到数组,然后将其发送到另一个视图控制器

在 PHP MVC 应用程序中将数据从控制器传递到视图

将数据从 didSelectRowAtIndexPath 传递到另一个视图控制器 ios7