SwiftUI显示图片列表时,如果显示多张图片,可能会导致内存过大,产生卡顿。
例如,显示20张高清壁纸图片。

当显示缩略图时,内存在37MB左右。当显示原图时,内存在600MB左右。
注:1张普通图片重复显示无法对比内存取消,需要多张高清图片才可以看出内存的显著变化。
因此,在图片列表过多以及图片较大的场景中,需要显示缩略图。
生成缩略图
1、根据Data生成缩略图
调用generateThumbnail方法,传入Data类型的图片和设置的大小:
private func generateThumbnail(from data: Data, maxSize: CGFloat) -> NSImage? {
guard let imageSource = CGImageSourceCreateWithData(data as CFData, nil),
let cgImage = CGImageSourceCreateThumbnailAtIndex(
imageSource,
0,
[
kCGImageSourceCreateThumbnailFromImageAlways: true,
kCGImageSourceThumbnailMaxPixelSize: maxSize,
kCGImageSourceCreateThumbnailWithTransform: true
] as CFDictionary
) else {
return nil
}
let size = NSSize(width: cgImage.width, height: cgImage.height)
let thumbnail = NSImage(cgImage: cgImage, size: size)
return thumbnail
}
SwiftUI生成缩略图:
if let original = NSImage(named: "文件\(i)"),
let data = original.tiffRepresentation,
let thumb = generateThumbnail(from: data, maxSize: 20) {
ArrayImg.append(ImageItem(image: Image(nsImage: thumb)))
}
创建NSImage图片,传入generateThumbnail生成图片缩略图。
2、根据图片URL创建缩略图
传入图片的URL和尺寸,生成缩略图。
private func generateThumbnail(from url: URL, maxSize: CGFloat) -> NSImage? {
guard let imageSource = CGImageSourceCreateWithURL(url as CFURL, nil),
let cgImage = CGImageSourceCreateThumbnailAtIndex(
imageSource,
0,
[
kCGImageSourceCreateThumbnailFromImageAlways: true,
kCGImageSourceThumbnailMaxPixelSize: maxSize,
kCGImageSourceCreateThumbnailWithTransform: true
] as CFDictionary
) else {
return nil
}
let size = NSSize(width: cgImage.width, height: cgImage.height)
let thumbnail = NSImage(cgImage: cgImage, size: size)
return thumbnail
}
与Data的区别在于,这里传入的是图片的URL。前者调用的是CGImageSourceCreateWithData创建CGImage,这里调用的是CGImageSourceCreateWithURL创建CGImage。
调用方法相同:
let thumb = generateThumbnail(from: url, maxSize: 20)
总结
对于压缩图片工具或展示图片列表时,需要考虑生成缩略图,防止图片过大造成内存过大的问题。
在实际使用中,还可以考虑懒加载图片。例如:
// 懒加载缩略图
private var _thumbnail: NSImage?
var thumbnail: NSImage? {
if _thumbnail == nil {
_thumbnail = generateThumbnail(from:inputURL,maxSize: 60)
}
return _thumbnail
}
当SwiftUI显示缩略图时,动态创建缩略图:
if let thumb = customImage.thumbnail {
Image(nsImage: thumb)
.resizable()
.frame(width: 60, height: 60)
}
当创建属性含有图片的对象时,不会因为图片导致内存过大,只在需要显示图片时,才会加载。
