我有以下代码下载一些图像,将它们复制到[UIImage]数组中,然后我应该从它们创建视图:
class ViewControllerGallery: UIViewController {
@IBOutlet weak var m_loader:UIActivityIndicatorView?
var m_num_images:Int = 0
var m_total_images:Int = 0
var m_str_json:String = ""
var m_images = [UIImage]()
var m_views = [UIImageView]()
override func viewDidLoad() {
super.viewDidLoad()
// BEGIN-CODE--4
self.m_loader?.startAnimating()
let jsonArray = jsonToArray(jsonString: m_str_json) //implementation of this function was omited for my post
if let _ = jsonArray {
m_total_images = (jsonArray?.count)!
}
for url in jsonArray! {
OpenImage(str_url: url)
}
if m_total_images != m_num_images {
print("the function finished without loading all images!")
} else {
self.m_loader?.stopAnimating()
}
}
func CreateViews()
{
print("createViews was called :) ")
}
func OpenImage(str_url:String) {
let url:URL = URL(string: str_url)!
getDataFromUrl(url: url){ data, response, error in
guard let data = data, error == nil else {return}
DispatchQueue.main.async {
let image = UIImage(data: data)
print(image!)
self.m_images.append(image!)
print(self.m_images.count)
self.m_num_images += 1
}
}
//2.3.3 Crear vistas cuando se han cargado todas las imagenes
print(self.m_total_images == self.m_num_images)
if self.m_total_images == self.m_num_images {
print("se igualan las imagenes")
self.performSelector(onMainThread: #selector(CreateViews), with: nil, waitUntilDone: false)
}
}
//Funciones auxiilares
func getDataFromUrl(url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> ()) {
URLSession.shared.dataTask(with: url) { data, response, error in
completion(data, response, error)
}.resume()
}
}
我希望从视图运行的代码确实加载,当它调用函数OpenImage到达行时,它应该执行OpenImage()中包含的整个代码。如果我运行它,控制台会显示(感谢print()语句)它会奇怪地前后跳转:
false
false
false
the function finished without loading all images!
<UIImage: 0x60c0000afc00>, {1024, 725}
1
<UIImage: 0x60c0000afcc0>, {960, 720}
2
<UIImage: 0x60c0000afc60>, {640, 801}
3
我希望它首先下载所有图像并将它们附加到m_images数组中。然后它应该执行performSelector方法并完成。
出于某种原因,正如你所看到的,它首先跳转到print语句,在那里它将加载的图像数量与图像总数进行比较,它会执行三次,然后它会超出for语句并打印“功能完成而不加载所有图像“然后它再次进入for循环并实际加载图像!为什么?如何以正确的顺序使其工作?
顺便说一下,我知道我应该使用camelCase,但是我已经收到了使用snake_case的类。
答案 0 :(得分:1)
您正在循环中调用OpenImages,它会进行异步调用。为了使循环能够正确地等待每次迭代返回,您可以在this post中完美地使用调度组。
您的代码应如下所示:
override func viewDidLoad() {
super.viewDidLoad()
// Some code...
let myGroup = DispatchGroup()
for url in jsonArray! {
myGroup.enter()
let url:URL = URL(string: str_url)!
// You can cut your OpenImages in two
// here we keep the part that has the async call
getDataFromUrl(url: url){ data, response, error in
guard let data = data, error == nil else {return}
DispatchQueue.main.async {
let image = UIImage(data: data)
print(image!)
self.m_images.append(image!)
print(self.m_images.count)
self.m_num_images += 1
myGroup.leave()
}
}
}
myGroup.notify(queue: .main) {
// All the async call have finished
// Here you can finish what you started in OpenImages()
// and the rest of your code: "if m_total_images != m_num_images"
}
}
答案 1 :(得分:0)
那是因为您正在调用异步API(函数)
getDataFromUrl(url: url)
异步API通常是那些需要更多时间来完成任务的网络功能。所以你的代码不会停在这一行。否则,您的UI将被此行阻止,这会提供较差的用户体验。而不是等待“getDataFromUrl”完成其工作,然后执行下一行代码。调用异步API可让您立即执行下一行。当“getDataFromUrl”最终完成其任务时,则执行下面的回调块
guard let data = data, error == nil else {return}
DispatchQueue.main.async {
let image = UIImage(data: data)
print(image!)
self.m_images.append(image!)
print(self.m_images.count)
self.m_num_images += 1
}