问题
I have an UICollectionView
which loads images of the iPad's memory and displays them in a grid,like Apple's Photos app. The UICollectionViewCell
loads thumbnails asynchronously:
func setImage(img:String){
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
//load the image in the background
let image = UIImage(contentsOfFile: img)
//when done, assign it to the cell's UIImageView
dispatch_async(dispatch_get_main_queue(), {
if let imageView = self.imageView{
imageView.image = UIImage(contentsOfFile: img)
}
})
})
}
However, while scrolling the view lags as if it is waiting for the images to load, especially with Retina graphics. The cells and images are about 240x180px big. Is there anything wrong with the image loading above or further optimisations need to be made?
UPDATE: Time profiler results

回答1:
You've already found that you're loading the UIImage
again on the main queue; fixing that will help.
UIImage
lazy loads its internal image data in most cases. One trick is to call its CGImage
property while still on the background queue to force it to actually create its internal image data instead of lazily loading it when the image view is drawn the first time:
func setImage(img:String){
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
//load the image in the background
let image = UIImage(contentsOfFile: img)
image.CGImage // <- Force UIImage to not lazy load the data
//when done, assign it to the cell's UIImageView
dispatch_async(dispatch_get_main_queue(), {
if let imageView = self.imageView {
imageView.image = image
}
})
})
}
Note: If you have a lot of images you may end up getting a memory warning fairly quickly doing this. If you do, this probably won't help because the memory warning will typically cause UIImage
to clear its internal image data again to free up resources.
回答2:
On the line
imageView.image = UIImage(contentsOfFile: img)
I was loading the image again on the main thread, not the image
loaded asynchronously. Changed to
imageView.image = image
Scrolling is a bit better, but yet choppy. The time profiler shows similar results as before. May the bottleneck be in the UIImageView drawing? It works fine with non-retina thumbnails.
回答3:
The bottleneck was that some thumbnails were scaled improperly and were a lot bigger than the UIImageView, which caused the longer loading time. I also presume this caused slower drawing since the UIImage had to be downscaled to fit in the UIImageView.
I fixed the thumbnail-generating code and scrolling is smooth again.
P.S. I also fixed the misplaced UIImage(contentsOfFile: img)
variable in the initial code, see my other answer.
回答4:
in Swift:
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority, 0)) {
let imagem = UIImage(named :"image")
dispatch_async(dispatch_get_main_queue()) {
cell.IMG_Novidades.image = imagem
}
}
//THIS IS VERY IMPORTANT
cell.layer.shouldRasterize = true
cell.layer.rasterizationScale = UIScreen.mainScreen().scale
来源:https://stackoverflow.com/questions/26186688/slow-uicollectionview-with-asynchronous-loading