概要
今天是 WWDC 2023 的第二天,我在张狂刷讲座的过程中突然瞥到了一个之前没用见的 API:preparingThumbnail(of:)
,所以略微研究了一下,感觉仍是十分香的,所以记下来分享一下。
图片烘托机制(精简版)
了解 iOS 烘托优化的同学一定刷过 WWDC 18 的一个瑰宝视频 Image and Graphics Best Practices (现在好像现已下掉了,但是其他平台有,没有看过的同学必看!)。视频中说到,图片烘托的本钱或许要比许多人想的大的多。许多人会觉得图片烘托占用的内存和图片占用的磁盘巨细有关,其实彻底不是。图片烘托的真实内容占用其实是和图片的尺度有关。一个被压缩到极致的图片,通过解码之后,或许会是一个尺度超级大的图片,而图片的每个像素点都占用固定的内存,图片尺度越大则像素越多,从而占用的内存也就更大。
因此苹果主张,假如图片尺度远大于实际烘托的尺度的话,能够运用下采样的方法,只烘托一个小尺度的图片。这样就能极大优化内容占用。
会有同学认为图片的烘托尺度和 UIImageView 的尺度相同,其实不是。尽管 UIImageView 限定了图片在屏幕上的烘托尺度,但图片在内存中是按完好尺度储存的。
优化图片烘托
读到这边许多同学或许就预备上手运用 UIGraphicsImageRenderer
把 UIImage
压缩到小尺度了。但其实这种方法本钱十分高,因为把图片读到 UIImage
的过程中其实现已是将完好图片尺度解码出来了,通过 UIGraphicsImageRenderer
转换之后尽管能得到一个较小的图片,但是中心额外的解码压缩过程功率很低,还会形成内存动摇。
苹果引荐运用 ImageIO
提供的更底层的 API 来烘托图片。这个 API 会直接从源文件中解码出指定尺度的图片数据,因此功率更高。一句话总结便是速度更快、内存占用更小。
import UIKit
import ImageIO
struct ImageIOConverter{
static func resize(url: URL)-> UIImage{
guard let imageSource = CGImageSourceCreateWithURL(url as CFURL, nil) else {
fatalError("Can not get imageSource")
}
let options: [NSString: Any] = [ kCGImageSourceThumbnailMaxPixelSize: 300, kCGImageSourceCreateThumbnailFromImageAlways: true ]
guard let scaledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options as CFDictionary) else {
fatalError("Can not get scaledImage")
}
return UIImage(cgImage: scaledImage)
}
}
不过 ImageIO
毕竟是偏底层的框架,代码的复杂度也很高。所以 Apple 在 WWDC21 推出了 preparingThumbnail
方法,能够方便快捷的生成指定巨细的图片,主张能用上的当地都用上。
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) as? ItemCell else {
fatalError("Unexpected type for cell. Check configuration.")
}
let item = items[indexPath.item] cell.nameLabel?.text = item.name
let thumbnail = item.image.preparingThumbnail(of: thumbnailSize)
cell.thumbnailImageView?.image = thumbnail return cell
}
这个 API 还交心的预备了三个版别:
- 同步版:
preparingThumbnail(of:)
- 异步版:
prepareThumbnail(of:completionHandler:)
- 协程版:
byPreparingThumbnail(ofSize:)
但坏消息是只支撑 iOS 15.0+ 。 另外 SwiftUI 这边也没有发现相似的 API ,看来只能用 UIImage 曲线救国了。
总结
当我发现这么重要的 API 竟然是 21 年发布的仍是挺吃惊的,近邻 Flutter 很早就上了 ResizeImage
,做的也是相同的事情。
另外现在许多公司为了优化图片烘托都会考虑在云端对图片做裁剪,但一来服务器本钱变高,二来 CDN 命中率也会下降。但优点是图片烘托压力变小了,带宽流量也少了。两种计划都是对烘托优化帮助很大的,我们能够借鉴一下。