从磁盘加载许多 UIImages 阻塞主线程 [英] Loading many UIImages from disk blocks main thread

查看:29
本文介绍了从磁盘加载许多 UIImages 阻塞主线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一组本地 UIImage ,当点击它们各自的单元格时,我需要按顺序加载和显示它们.例如,我有 20 张热狗图像,它们组合在一起形成一个动画.当用户点击热狗单元格时,单元格的 UIImageView 应该为图像设置动画.

I have a group of local UIImages 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.

我知道如何使用UIImageViewanimationImages 来实现动画.我的问题是从磁盘检索所有这些图像需要大约 1.5 秒并阻塞主线程.

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.

我可以在 application(_:didFinishLaunchingWithOptions:) 中实例化一个辅助类,它在后台线程上从磁盘加载这些图像,以便在需要时它们会在内存中,但这似乎很麻烦.

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?

这些图像是插图,因此是 .png.

These images are illustrations and thus are .png.

Edit2:假设每个图像序列的总和为 1 MB.我正在测试的图像尺寸比 UIImageView 的 @3x 要求大 33-60%.在从我们的设计师那里获得正确的图像集之前,我正在等待确认最终的 UIImageView 大小,因此使用适当大小的资源应该可以大大缩短时间,但我也在测试实体 iPhone X.

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()

    }
}

推荐答案

我建议你试试 UIImage(contentsOfFile:) 而不是 UIImage(named:).在我的快速测试中,发现它快了一个数量级以上.这有点可以理解,因为它做了很多事情(搜索资产、缓存资产等).

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)
}

注意,这假定您在资源目录中有这些文件,您可能需要根据它们在项目中的位置进行相应的修改.另请注意,我避免在循环内执行 Bundle.main.url(forResource:withExtension:) ,因为即使这样也会对性能产生明显影响.

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.

这篇关于从磁盘加载许多 UIImages 阻塞主线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆