问题
I have a group of local UIImage
s that I need to load and present in successive order when their respective cell is tapped. For example, I have 20 images of a hot dog that combine to form an animation. When the user taps the hot dog cell, the cell's UIImageView
should animate the images.
I know how to use UIImageView
's animationImages
to achieve the animation. My problem is that retrieving all of these images from disk takes ~1.5 seconds and blocks the main thread.
I could instantiate a helper class in application(_:didFinishLaunchingWithOptions:)
that loads these images from disk on a background thread so that they'll be in memory when needed, but this seems hacky.
Are there any better ways of quickly loading many images from disk?
Edit: These images are illustrations and thus are .png.
Edit2: Assume the sum of each image sequence is 1 MB. The image dimensions I'm testing with are 33-60% larger than the UIImageView
's @3x requirements. I am waiting to confirm final UIImageView
size before getting correct image sets from our designers, so the time should be cut significantly with properly sized assets, but I'm also testing on a physical iPhone X.
class ViewModel {
func getImages() -> [UIImage] {
var images: [UIImage] = []
for i in 0..<44 {
if let image = UIImage(named: "hotDog\(i).png") {
images.append(image)
}
}
return images
}
}
class ViewController: UIViewController {
private var viewModel: ViewModel!
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as! CustomCell
let images = viewModel.getImages()
cell.animateImageView(withImages: images)
}
}
class CustomCell: UITableViewCell {
@IBOutlet weak var imageView: UIImageView!
func animateImageView(withImages images: [UIImage]) {
imageView.image = images.last
imageView.animationImages = images
imageView.animationDuration = TimeInterval(images.count / 20)
imageView.animationRepeatCount = 1
imageView.startAnimating()
}
}
回答1:
I would suggest you try UIImage(contentsOfFile:)
instead of UIImage(named:)
. In my quick test and found it to be more than an order of magnitude faster. It's somewhat understandable because it's doing a lot more (searching for the asset, cacheing the asset, etc.).
// slow
@IBAction func didTapNamed(_ sender: Any) {
let start = CFAbsoluteTimeGetCurrent()
imageView.animationImages = (0 ..< 20).map {
UIImage(named: filename(for: $0))!
}
imageView.animationDuration = 1.0
imageView.animationRepeatCount = 1
imageView.startAnimating()
print(CFAbsoluteTimeGetCurrent() - start)
}
// faster
@IBAction func didTapBundle(_ sender: Any) {
let start = CFAbsoluteTimeGetCurrent()
let url = Bundle.main.resourceURL!
imageView.animationImages = (0 ..< 20).map {
UIImage(contentsOfFile: url.appendingPathComponent(filename(for: $0)).path)!
}
imageView.animationDuration = 1.0
imageView.animationRepeatCount = 1
imageView.startAnimating()
print(CFAbsoluteTimeGetCurrent() - start)
}
Note, this presumes that you had the files in the resource directory, and you may have to modify this accordingly depending upon where they are in your project. Also note that I avoided doing Bundle.main.url(forResource:withExtension:)
within the loop, because even that had an observable impact on performance.
来源:https://stackoverflow.com/questions/49470039/loading-many-uiimages-from-disk-blocks-main-thread