从磁盘加载许多 UIImages 阻塞主线程
Loading many UIImages from disk blocks main thread
我有一组本地 UIImage
,当它们各自的单元格被点击时,我需要按顺序加载和显示它们。例如,我有 20 张热狗图像组合在一起形成动画。当用户点击热狗单元格时,该单元格的 UIImageView
应该会激活图像。
我知道如何使用UIImageView
的animationImages
来实现动画。我的问题是从磁盘中检索所有这些图像需要大约 1.5 秒并阻塞主线程。
我可以在 application(_:didFinishLaunchingWithOptions:)
中实例化一个帮助程序 class,它在后台线程上从磁盘加载这些图像,以便它们在需要时位于内存中,但这看起来很老套。
有没有更好的方法可以从磁盘快速加载许多图像?
编辑:这些图像是插图,因此是 .png。
Edit2:假设每个图像序列的总和为1 MB。我正在测试的图像尺寸比 UIImageView
的 @3x 要求大 33-60%。我 正在 等待确认最终 UIImageView
大小,然后再从我们的设计师那里获得正确的图像集,因此使用适当大小的资产应该会大大缩短时间,但我也在测试物理 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:)
。在我的快速测试中,发现它的速度快了一个数量级以上。它有点好理解,因为它做了更多(搜索资产、缓存资产等)。
// slow
@IBAction func didTapNamed(_ sender: Any) {
let start = CFAbsoluteTimeGetCurrent()
imageView.animationImages = (0 ..< 20).map {
UIImage(named: filename(for: [=10=]))!
}
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: [=10=])).path)!
}
imageView.animationDuration = 1.0
imageView.animationRepeatCount = 1
imageView.startAnimating()
print(CFAbsoluteTimeGetCurrent() - start)
}
注意,这假定您在资源目录中有文件,您可能必须根据它们在项目中的位置相应地修改它。另请注意,我避免在循环内执行 Bundle.main.url(forResource:withExtension:)
,因为即使那样也会对性能产生明显的影响。
我有一组本地 UIImage
,当它们各自的单元格被点击时,我需要按顺序加载和显示它们。例如,我有 20 张热狗图像组合在一起形成动画。当用户点击热狗单元格时,该单元格的 UIImageView
应该会激活图像。
我知道如何使用UIImageView
的animationImages
来实现动画。我的问题是从磁盘中检索所有这些图像需要大约 1.5 秒并阻塞主线程。
我可以在 application(_:didFinishLaunchingWithOptions:)
中实例化一个帮助程序 class,它在后台线程上从磁盘加载这些图像,以便它们在需要时位于内存中,但这看起来很老套。
有没有更好的方法可以从磁盘快速加载许多图像?
编辑:这些图像是插图,因此是 .png。
Edit2:假设每个图像序列的总和为1 MB。我正在测试的图像尺寸比 UIImageView
的 @3x 要求大 33-60%。我 正在 等待确认最终 UIImageView
大小,然后再从我们的设计师那里获得正确的图像集,因此使用适当大小的资产应该会大大缩短时间,但我也在测试物理 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:)
。在我的快速测试中,发现它的速度快了一个数量级以上。它有点好理解,因为它做了更多(搜索资产、缓存资产等)。
// slow
@IBAction func didTapNamed(_ sender: Any) {
let start = CFAbsoluteTimeGetCurrent()
imageView.animationImages = (0 ..< 20).map {
UIImage(named: filename(for: [=10=]))!
}
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: [=10=])).path)!
}
imageView.animationDuration = 1.0
imageView.animationRepeatCount = 1
imageView.startAnimating()
print(CFAbsoluteTimeGetCurrent() - start)
}
注意,这假定您在资源目录中有文件,您可能必须根据它们在项目中的位置相应地修改它。另请注意,我避免在循环内执行 Bundle.main.url(forResource:withExtension:)
,因为即使那样也会对性能产生明显的影响。