在讲解如何选择图片格式以前,我感受有必要先了解下,图片是如何展现的。若是咱们要展现一张图片,通常步骤是这样的:html
/// Assets.xcassets中的图片,不须要后缀
let image = UIImage(named: "icon")
let imageView = UIImageView(frame: rect)
imageView.image = image
view.addSubview(imageView)
复制代码
运行程序,咱们就能够在指定位置看到这个icon。看似简单的代码背后隐藏了不少细节工做。一张图片的展现,从代码执行到展现出来大体经历了这些步骤:ios
1. 加载图片git
从磁盘中加载一张图片;github
而后将生成的 UIImage
赋值给 UIImageView
;web
接着一个隐式的 CATransaction
捕获到了 UIImageView
图层树的变化;算法
分配内存缓冲区用于管理文件 IO 和解压缩操做,将文件数据从磁盘读到内存中;swift
2. 图片解码(解压)xcode
3. 图片渲染缓存
Core Animation
中CALayer
使用解压(解码)的位图数据渲染 UIImageView
的图层;bash
CPU计算好图片的Frame,对图片解压以后,就会交给GPU来作图片渲染渲染流程;
GPU获取获取图片的坐标,将坐标交给顶点着色器(顶点计算),将图片光栅化(获取图片对应屏幕上的像素点),片元着色器计算(计算每一个像素点的最终显示的颜色值);
从帧缓存区中渲染到屏幕上;
这其中有个关键步骤是图片解码。那为何要解码呢,这是由于咱们日常使用的图片通常为了节约空间都会通过一些压缩算法进行封装,而使用时屏幕要精确的渲染到每一个像素点,这就须要把压缩的图片解码展开,便于系统处理。
有损压缩:指在压缩文件大小的过程当中,损失了一部分图片的信息,也即下降了图片的质量,而且这种损失是不可逆的,咱们不可能从有一个有损压缩过的图片中恢复出全来的图片。常见的有损压缩手段,是按照必定的算法将临近的像素点进行合并。
无损压缩:只在压缩文件大小的过程当中,图片的质量没有任何损耗。咱们任什么时候候均可以从无损压缩过的图片中恢复出原来的信息。
索引色:用一个数字来表明(索引)一种颜色,在存储图片的时候,存储一个数字的组合,同时存储数字到图片颜色的映射。这种方式只能存储有限种颜色,一般是256种颜色,对应到计算机系统中,使用一个字节的数字来索引一种颜色。
直接色:使用四个数字来表明一种颜色,这四个数字分别表明这个颜色中红色、绿色、蓝色以及透明度。如今流行的显示设备能够在这四个维度分别支持256种变化,因此直接色能够表示2的32次方种颜色。固然并不是全部的直接色都支持这么多种,为压缩空间使用,有可能只有表达红、绿、蓝的三个数字,每一个数字也可能不支持256种变化之多。
点阵图:也叫作位图,像素图。构成点阵图的最小单位是象素,位图就是由象素阵列的排列来实现其显示效果的,每一个象素有本身的颜色信息,在对位图图像进行编辑操做的时候,可操做的对象是每一个象素,咱们能够改变图像的色相、饱和度、明度,从而改变图像的显示效果。点阵图缩放会失真,用最近很是流行的沙画来比喻最恰当不过,当你从远处看的时候,画面细腻多彩,可是当你靠的很是近的时候,你就能看到组成画面的每粒沙子以及每一个沙粒的颜色。
矢量图:也叫作向量图。矢量图并不纪录画面上每一点的信息,而是纪录了元素形状及颜色的算法,当你打开一张矢量图的时候,软件对图形象对应的函数进行运算,将运算结果[图形的形状和颜色]显示给你看。不管显示画面是大仍是小,画面上的对象对应的算法是不变的,因此,即便对画面进行倍数至关大的缩放,其显示效果仍然相同(不失真)。
一张图片,若是咱们将它的每个像素及其对应的颜色都存储起来(BMP格式),是会很大的。为了减少图片占用的存储空间,派生出了各类不一样压缩算法所表明的图片格式。常见的图片格式有png、jpeg、heic、gif、webp,svg等。
PNG有两种类型:PNG-8和PNG-24。
PNG-8是无损的、索引色、点阵图。它支持透明度的调节。
PNG-24是无损的、直接色、点阵图。由于使用直接色,颜色范围更大,也占用更大的空间。他的目标是替代JPEG,但通常而言,PNG-24的文件大小是JPEG的五倍之多,而显示效果则一般只能得到一点点提高。因此若是不是对图片质量要求特别高,不建议使用PNG-24
PNG是苹果推荐的图片格式,并且这些图片会被一个叫pngcrush的开源工具优化,这样iOS设备就能在显示时更快的解压和渲染图片。该工具位于目录:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin
复制代码
JPEG是有损的、采用直接色的、点阵图压缩方式。 JEPG目标是在不影响人类可分辨的图片质量的前提下,尽量的压缩文件大小。通常都是用于相机图片的格式。但由于有损,会致使图片失真。iOS能够经过如下方式压缩图片:
// 压缩比范围从0到1
func jpegData(compressionQuality: CGFloat) -> Data?
复制代码
题外话 通常来讲,相同的图片采用JPEG的压缩方式会比png获得更小的尺寸,但也有例外。
在网上查了资料说是JEPG更适合处理带有不少杂色的风景图,而对于使用数位板等电子绘制的纯色卡通系风格图片,JEPG的压缩方式会拔苗助长,致使体积更大。
HEIC是HEIF(High Efficiency Image Format 高效率图像文件格式)的一种。它并不是苹果开发,而是由运动图像专家组(MPEG)开发。它同时支持有损压缩、无损压缩、透明度等特性。HEIF规范的完成是在2015年,是这几种图片格式中最新的一种了,目前除了苹果,尚未哪家大厂去拥抱这种格式。
在iOS 11更新后,iPhone 7及其后硬件,在拍摄照片时的默认图像存储格式。与JPG相比,它占用的空间更小,画质更加无损。HEIC的目的就是做为JPEG的继任者,之后或许会成为一种趋势。目前能够想到的在开发中的应用是,对于一些须要下载的大图能够转成HEIC格式,供客户端使用。可是当前却不多应用,大几率是考虑到图片兼容问题吧。
题外话
一个有趣的现象,我用相机(iPhoneXR)拍摄一张照片,经过AirDrop传到电脑,显示为HEIC格式。当我在拍照时选择系统自带的任意一种滤镜,图片格式就变成了JPEG。这是为何?
有小伙伴解答:
iOS拍照选择滤镜会“转”为JPEG,是由于拍照的格式仍是HEIF,加滤镜和编辑图片都是至关于复制了一份再作操做的,点击复原又会“转”为HEIF。
通过测试确实是这样的,并且既然苹果提供复原的操做,说明原图(HEIF)并无被覆盖。那为何滤镜不能直接在HEIF格式下操做,猜想多是跟滤镜的算法相关,该算法只能对JEPG格式编码的图片进行渲染,因此须要中间转成JEPG。
Live Photo
Live图片的实质是:一张heic格式封面图 + mov格式视频。
对于Live Photo的展现,在原生应用中可使用PHLivePhotoView,在Web应用中可使用LivePhotosKit JS。
WebP最初由Google发布于2010年,图片格式派生自VP8视频编码,也同时支持有损压缩、无损压缩、透明度等特性。2013年低,推出了Animated WebP,还能够支持动图。
WebP 集合了多种图片文件格式的特色。它像 JPEG 同样适合压缩照片和其余细节丰富的图片,像 GIF 同样能够显示动态图片,像 PNG 同样支持透明图像。根据 Google 的测试,WebP 无损压缩图片比 PNG 图片少了 45% 的文件体积,即便这些 PNG 图片在使用 pngcrush
和 PNGOUT
处理后,WebP 依旧能够减小 28% 的文件体积。能够在点击这里查看WebP对其它格式转换的效果。
小是WebP的最大优势,小意味着更少的流量,这也是各大流量入口在乎的地方。目前Google、Facebook、阿里等大厂已经在普遍使用WebP格式,国内的一些图床服务(七牛、又拍云)也支持将图片自动转成WebP格式。
诚然WebP很是优秀,独自完成了图片格式"大一统"的任务。但苹果对WebP的支持却不多,只Safari目前还不支持WebP显示就阻断了不少人应用WebP的决心。
若是咱们须要在项目中显示WebP格式图片就不得不导入Google的libwebp
解码库。固然WebP的解码任务在iOS端有些库已经封装好了,OC端能够用SDWebImageWebPCoder,Swift端能够用KingfisherWebP。如下是使用Kingfisher展现WebP图像的事例:
// 全局配置对WebP图片的解码(仅针对WebP格式)
KingfisherManager.shared.defaultOptions += [
.processor(WebPProcessor.default),
.cacheSerializer(WebPSerializer.default)
]
// 本地webp图片解码
let localUrl = Bundle.main.url(forResource: "sunset", withExtension: "webp")
let dataProvider = LocalFileImageDataProvider.init(fileURL: localUrl!)
imageView.kf.setImage(with: dataProvider)
// 远程webp图片解码。一些图像服务器可能指望“Accept”标头包含“image/webp”,咱们还须要加上
let modifier = AnyModifier { request in
var req = request
req.addValue("image/webp */*", forHTTPHeaderField: "Accept")
return req
}
KingfisherManager.shared.defaultOptions += [
.requestModifier(modifier),
// ... other options
]
复制代码
pdf图片一般是矢量的,它的导入方式有些特殊。咱们须要在Assets.xcassets
文件,建立一个New Image Set
,而后将该文件的Scales
设置为Single Scale
,拖入1x尺寸的pdf文件便可:
使用时咱们能够把它当作普通图片对待:
let image = UIImage(named: "sunset")
复制代码
在运行期间Xcode会根据屏幕的比例因子生成对应尺寸的png图像。好比导入一张100x100的pdf图片,在2x和3x的机型里面会生成对应的200x200,300x300的png(能够在Assets.car中找到)。因此pdf只不过是Xcode处理图片的中间状态,下载到手机的应用包里面是没有这张pdf的。
这种处理方式有一个好处就是,当苹果之后发布一款4x屏幕的手机时,使用pdf处理的图片会自适应生成对应的4x资源,不须要再手动导入。但相比优势,pdf做为图片资源的缺点更多。
首先是尺寸上,由于是自动生成对应的png,并无任何优化和压缩,并且咱们也并不能在这中间作什么。对比相同尺寸通过ImageOptim压缩过的png,在大小上后者会是前者的1/2,甚至1/4。
另外pdf对阴影和渐变的处理会存在失真的状况:
左边是png,右边是pdf。在一些渐变和光影的图像部分能够看出明显的失真。
更多关于pdf和png的差异,能够看这篇:Why I don't use PDFs for iOS assets: bjango.com/articles/id…
SVG是一种无损的矢量图,是众多矢量图中的一种,它的特色是使用XML来描述图片。使用XML的优势是,任什么时候候你均可以把它当作一个文本文件来对待,也就是说,你能够很是方便的修改SVG图片,你所须要的只须要一个文本编辑器。
在iOS13以前应用中直接使用SVG的场景很是少,但从iOS13开始,苹果推出了SF Symbol,一种svg格式的矢量符号集。并且苹果还提供了多于1500多种icon模板,咱们能够在这里下载查看。
咱们能够从中选择适合本身的icon,选中以后,从File > Export Custom Symbol Templete中导出svg格式图片集,而后拖到Xcode的Assets.xcassets
。
SF Symbol有9种粗细的调节——从ultralight到black——每一种都至关于San Francisco
系统字体的重量(weight)。(SF Symbol中的SF是San Francisco
(旧金山)的缩写)。这种对应使您可以在符号和相邻文本之间实现精确的权重匹配,同时支持不一样大小和上下文的灵活性。
固然若是这些图标都不能知足需求,咱们还能够自定义SF图标,而后经过SF Symbol App进行验证和导出。操做细节能够看这里:Creating Custom Symbol Images for Your App。
SF Symbol使用起来也很简单:
let configuration = UIImage.SymbolConfiguration.init(scale: .small)
imageView.image = UIImage(systemName: "alarm", withConfiguration: configuration)
复制代码
SF Symbol能够一次性解决相同icon,不一样尺寸,不一样粗细的问题,它让咱们处理图片像处理字体同样方便。能够想象这就是应用图标的将来。
当看到SF Symbol仅支持iOS13+,watchOS6+,我又不得不退回到现实,png也挺好的。
题外话
我在测试SF Symbol图标时,从生成的应用包中查看图片,会获得这样的结果:
代码中的我将图片设置为100x100,仅有这一处地方使用。跟pdf相似咱们找不到svg源文件,这好理解,svg只是中间状态,咱们最终使用的仍是png,但为何会有多个小尺寸的png图像呢?
咱们日常开发时,使用最多的就是png了,甚至多是不加考虑的所有使用png。其实这样是很差的,咱们应该充分发挥不一样格式图片的优势,从兼容性、空间占用、展现效果三方面考量选取最佳格式。
关于图片格式的选择,苹果的Human Interface Guidelines有如下说法:
通常状况下,使用PNG图片,由于PNG支持透明性,并且是无损的,压缩工件不会模糊重要的细节或改变颜色。对于须要阴影、纹理和高光效果的复杂艺术品来讲,这是一个不错的选择。使用8位的PNG图形,不须要彻底24位的颜色。8位PNG能够在不下降图像质量的状况下减少文件大小。精致的应用图标最好使用png。
对于照片应该使用JPEG格式,它的压缩算法一般比无损格式产生更小的尺寸,并且很难在照片中辨别出来。应该尝试优化JPEG文件,在大小和质量之间找到平衡。大多数JPEG文件能够被压缩而不会致使图像明显的退化。即便是少许的压缩也能够节省大量的磁盘空间。
使用PDF处理字形和其余须要高分辨率缩放的平面矢量图形。
最终能够作如下总结。
图片格式 | 适用范围 | 注意事项 |
---|---|---|
png | 应用icon,界面icon,卡通风格的背景图 | 导入项目前可使用ImageOptim进行压缩 |
jpeg | 尺寸较大的风景图,照片 | 不支持透明度;由于能够调节压缩比,能够在大小和质量之间寻找最佳平衡。 |
webp | 支持有损、无损压缩、透明度、动图等特性,由于苹果自己不支持通常只应用于服务端返回来的图片 | 没法在xcode预览,不建议内置该类型图片 |
字形,高分辨率的矢量图 | 存在展开尺寸较大,光效失真的状况 | |
svg(sf symbol) | 指示性icon | 仅支持iOS13及以上,系统sf符号是有版权的,使用时要注意应用范围和苹果要求 |
Why I don't use PDFs for iOS assets