关于在用户查询后将数据从 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 将用户输入数据从视图传递到控制器