我有一个像这样的视图控制器:
有一个ImageView和一个RangeSlider。
行为应该是这样的: 如果用户滑动范围滑块,ImageView中的图像应切换到图像数组内的新图像。用户还可以对图像进行缩放操作而不会损失图像的质量。
所以我做的是将图像下载到viewDidLoad
上的图像数组中:
//image array
var images = [UIImage]()
我正在下载48张图像,大小为1920×1920像素,每张图像大约120 KB,并创建下载数据的UIImage
个对象,并将它们保存到数组中,如下所示:
DispatchQueue.global().async { [weak self] in
let data = try? Data(contentsOf: url)
DispatchQueue.main.async {
if data != nil {
let img = UIImage(data: data!)
self?.images.append(img!)
} else {
...
}
}
}
要更改imageView中的图像,我创建了此功能:
func updateImage(currentImage: UIImage) {
self.imageView.image = currentImage
}
当用户开始滑动rangeSlider
时,应用程序的内存使用量从40 MB运行到700 MB,并在用户切换所有图像后停止增加。在此之后,用户可以前后切换整个图像,而不会影响内存使用。
但是当我在更新图像之前稍微调整大小时,内存问题会得到解决,但如果用户放大一点,图像现在质量很低。
调整部分:
extension UIImage {
func resized(_ newSize:CGSize) -> UIImage {
UIGraphicsBeginImageContextWithOptions(newSize, false, 0)
self.draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}
func updateImage(currentImage: UIImage) {
let currentImg = currentImage.resized(self.imageView.bounds.size)
self.imageView.image = currentImg
}
对于缩放我用这个:
@IBAction func scaleImage(_ sender: UIPinchGestureRecognizer) {
print(self.imageView.transform)
let tmp = self.imageView.transform.scaledBy(x: sender.scale, y: sender.scale)
if (tmp.a >= 0.99 || tmp.d >= 0.99) && (tmp.a < 2.5 || tmp.d < 2.5) {
self.imageView.transform = self.imageView.transform.scaledBy(x: sender.scale, y: sender.scale)
sender.scale = 1
if (tmp.a <= 1.2 || tmp.d <= 1.2) {
self.imageView.center = defaultImageCenter
}
}
}
那么我怎样才能以优雅的方式实现它,不会造成内存泄漏,但仍能使用最佳的图像质量?
答案 0 :(得分:2)
if data != nil {
let img = UIImage(data: data!)
self?.images.append(img!)
}
在这部分中,不是创建UIImage并将其添加到数组,而是将数据保存到磁盘并将其位置存储为数组。然后当您移动滑块时,从与滑块位置匹配的位置创建UIImage。这样一次只能在内存中加载一个UIImage。
如果你想让它更快,也可以加载上一张和下一张图片(例如你在第4位,加载图像3和5,然后当你将滑块移动到第5位时,你就已经拥有了图像 - 此时释放图像3并加载6)。这将占用更大的内存,但是当您移动滑块时,您将立即看到图像。
您可能还希望不要一次下载所有图片以加快速度,但这与内存无关,因此这应该足以解决您的问题。