i am trying to load set of images from the server and updating UI with returned images and displaying it by fade animation. it will be repeated forever.
Code snippet:
override func viewDidLoad(animated: Bool) {
super.viewDidLoad(animated)
self.loadImages()
}
func loadImages(){
var urlImage:UIImage?
//self.array_images contains preloaded images (not UIImage) objects which i get from different api call.
if imageCount >= self.array_images!.count{
imageCount = 0
}
var img = self.array_images[imageCount]
var url = NSURL(string: img.url!)
var data = NSData(contentsOfURL: url!)
if (data != nil){
urlImage = UIImage(data: data!)
UIView.transitionWithView(self.imageView_Promotion, duration: 1.2, options: UIViewAnimationOptions.TransitionCrossDissolve, animations: {
println("Begin Animation")
self.imageView_Promotion.image = realImage
}, completion:{ finished in
println("Completed")
self.imageCount++
self.loadImages() //another issue: it calls the function more than couple of times
})
}
}else{
var image:UIImage = UIImage(named: "test_Image.jpg")!
imageView_Promotion.image = image
}
The above one hangs the UI. i have tried to call loadImages in dispatch_async and animation in dispatch_main queue but the issue still persist.
let priority = DISPATCH_QUEUE_PRIORITY_BACKGROUND
dispatch_async(dispatch_get_global_queue(priority, 0)) {
self.loadImages()
}
dispatch_async(dispatch_get_main_queue(),{
//UIView animation
})
What is the proper way to handle this.
Thanks
Basically, UIKit APIs are not thread-safe. It means UIKit APIs should be called only from the main thread (the main queue). But, actually, there are some exceptions. For instance, UIImage +data: is thread-safe API.
Your code includes some unsafe calls.
UIView.transitionWithView(self.imageView_Promotion, duration: 1.2, options: UIViewAnimationOptions.TransitionCrossDissolve, animations: {
I don't think UIView.transitionWithView is thread-safe.
var image:UIImage = UIImage(named: "test_Image.jpg")!
UIImage +named: is not thread-safe, it uses a global cache or so on. According to UIImage init(named:) Discussion.
You can not assume that this method is thread safe.
If the thread-safe problem was fixed, it would block the main thread.
Your code calls loadImages method, that downloads an image from the server or loads an image from the file system, from the completion block of UIView.transitionWithView. UIView.transitionWithView should be called from the main thread, so the completion block also will be called from the main thread. It means downloading or loading an image on the main thread. It blocks the main thread.
Thus, loadImages should be like the following.
Swift Playground code:
import UIKit
import XCPlayground
func loadImage(file:String) -> UIImage {
let path = NSBundle.mainBundle().pathForResource(file, ofType: "")
let data = NSData(contentsOfFile: path!)
let image = UIImage(data: data!)
return image!
}
var index = 0
let files = ["image0.png", "image1.png", "image2.png"]
let placementImage = loadImage(files[0])
let view = UIImageView(image:placementImage)
// loadImages function
func loadImages() {
let priority = DISPATCH_QUEUE_PRIORITY_BACKGROUND
dispatch_async(dispatch_get_global_queue(priority, 0)) {
/*
* This block will be invoked on the background thread
* Load an image on the background thread
* Don't use non-background-thread-safe APIs like UIImage +named:
*/
if index >= files.count {
index = 0
}
var file = files[index++]
let image = loadImage(file)
dispatch_async(dispatch_get_main_queue()) {
/*
* This block will be invoked on the main thread
* It is safe to call any UIKit APIs
*/
UIView.transitionWithView(view, duration: 1.2, options: UIViewAnimationOptions.TransitionCrossDissolve, animations: {
/*
* This block will be invoked on the main thread
* It is safe to call any UIKit APIs
*/
view.image = image
}, completion:{ finished in
/*
* This block will be invoked on the main thread
* It is safe to call any UIKit APIs
*/
loadImages()
})
}
}
}
// call loadImages() from the main thread
XCPShowView("image", view)
loadImages()
XCPSetExecutionShouldContinueIndefinitely()