此代码的目的是,当您双击MapKit视图时,它将在地图上放置一个图钉并获取经度和纬度坐标。当它放下引脚时,我希望它调用API并让该API返回城市名称。我想通过在地图视图上放置标签来显示城市名称,只要您放置新的图钉,该标签就会更新。
当引脚掉线时,此功能称为:
func previewDataOnButton(){
print("PreviewDataOnButton Called")
theWeatherDataModel.callAPI(latitude: latitude!, longitude: longitude!)
cityStateLabel.text = theWeatherDataModel.cityName
print("PreviewDataOnButton Finished")
}
此函数在视图控制器中,它在单独的模型中调用一个函数。它正在调用的函数如下:
func callAPI(latitude: String, longitude: String){
let baseURL = "https://api.weatherbit.io/v2.0/current?&lat=\(latitude)&lon=\(longitude)&key=\(apiKey)"
let urlComponents = URLComponents(string: baseURL)
let theURL = urlComponents?.url
let session = URLSession(configuration: .ephemeral)
let theTask = session.dataTask(with: theURL!) {
(data, response, error) in
if let actualError = error{
print("We got an error")
} else if let actualData = data, let actualResponse = response{
print("We got stuff back")
let parsedData = try? JSON(data: actualData)
//print("Data: \n\(String(describing: parsedData))")
if let theWeatherArray = parsedData?["data"]{
for(_, singleWeatherDictionary) in theWeatherArray{
self.cityName = singleWeatherDictionary["city_name"].string
}
}
} else {
print("How did I get here?")
}
print("Im done with the closure")
}
print("About to start the data task")
theTask.resume()
print("Started data task")
}
我在整个代码中放置了打印语句以进行调试,并打印了以下内容:
调用了PreviewDataOnButton
关于开始数据任务
已启动数据任务
PreviewDataOnButton完成
我们回来了
我已经结束了
从输出来看,在模型中的函数可以完成其任务并调用模型之前,视图控制器中的函数似乎已完成。这会导致视图上的标签无法正确使用正确的城市名称进行更新,因为该函数在API实际获得城市名称之前便已完成。这是我遇到的问题,我们将不胜感激。
答案 0 :(得分:1)
您应该等待api调用完成,然后更新UI。睡觉对你没有帮助。您可以将UI更新部分作为完成块(闭包)传递给api调用函数,并在调用完成后调用它。您最终可能会得到类似于以下的代码。
func previewDataOnButton(){
print("PreviewDataOnButton Called")
theWeatherDataModel.callAPI(latitude: latitude!, longitude: longitude!) { [weak self] completed in
guard let self = self else {
return
}
DispatchQueue.main.async {
self.cityStateLabel.text = self.theWeatherDataModel.cityName
print("PreviewDataOnButton Finished")
}
}
}
func callAPI(latitude: String, longitude: String, completionBlock: @escaping (Bool) -> Void){
let baseURL = "https://api.weatherbit.io/v2.0/current?&lat=\(latitude)&lon=\(longitude)&key=\(apiKey)"
let urlComponents = URLComponents(string: baseURL)
let theURL = urlComponents?.url
let session = URLSession(configuration: .ephemeral)
let theTask = session.dataTask(with: theURL!) {
(data, response, error) in
if let actualError = error{
print("We got an error")
completionBlock(false)
} else if let actualData = data, let actualResponse = response{
print("We got stuff back")
let parsedData = try? JSON(data: actualData)
//print("Data: \n\(String(describing: parsedData))")
if let theWeatherArray = parsedData?["data"]{
for(_, singleWeatherDictionary) in theWeatherArray{
self.cityName = singleWeatherDictionary["city_name"].string
}
}
completionBlock(true)
} else {
print("How did I get here?")
}
print("Im done with the closure")
}
print("About to start the data task")
theTask.resume()
print("Started data task")
}
答案 1 :(得分:0)
您需要将完成处理程序添加到 callAPI 中,以等待功能结束。并且在函数中,您需要使用 DispatchGroup 来保持该函数,直到循环结束。 (您的WeatherWeatherArray的项目很少,这没问题。但是,当有大量项目时,它将发出问题)
func callAPI(latitude: String, longitude: String, completionHandler: @escaping(Result<Bool,Error>)->Void){
let baseURL = "https://api.weatherbit.io/v2.0/current?&lat=\(latitude)&lon=\(longitude)&key=\(apiKey)"
let urlComponents = URLComponents(string: baseURL)
let theURL = urlComponents?.url
let session = URLSession(configuration: .ephemeral)
let theTask = session.dataTask(with: theURL!) {
(data, response, error) in
if let actualError = error{
completionHandler(.failure(actualError))
} else if let actualData = data, let actualResponse = response{
let dGroup = DispatchGroup()
let parsedData = try? JSON(data: actualData)
if let theWeatherArray = parsedData?["data"]{
for(_, singleWeatherDictionary) in theWeatherArray{
dGroup.enter()
self.cityName = singleWeatherDictionary["city_name"].string
dGroup.leave()
}
}
dGroup.notify(queue: .main) {
completionHandler(.success(true))
}
} else {
completionHandler(.success(false))
}
}
theTask.resume()
}
现在您可以像这样调用此函数
callAPI(latitude: "", longitude: "") { (response) in
switch response{
case .success(let granted):
if granted{
//success true
cityStateLabel.text = theWeatherDataModel.cityName
}else{
//success false
}
case .failure(_):
//error
}
}