iOS关于@2x,@3x的区别及图片的绘制
为什么会有2倍,3倍图
网络上会有很多的解释,这些解释都是对的。
通俗点说,出现这种区别是因为,屏幕尺寸没有屏幕分辨率增长的快(所以更高清了)。所以,对于iPhone6来说,1pt(代码中的尺寸值单位,也可理解为屏幕物理尺寸的换算单位)代表2px。对于iPhone6P来说,1pt代表3px。
所以对于iPhone6P和iPhone6来说,我们如果都想在屏幕上显示一个100ptx100pt的图片。
iPhone6就需要加载一个尺寸 200px x 200px 的图片。
iPhone6P需要加载一个尺寸 300px x 300px 的图片。
因为如果iPhone6加载一个100px x 100px的图片,它实际上要填充的是200px x 200px像素的空间。所以会被放大,看起来就模糊了。
分2倍3倍图,是为了让不同设备显示图片能够更加清晰。
但是我们开发的时候,设计师通常会将3种大小的图都输出出来,我们也通常选择将3种规格的图片都拖到工程中(现在1x的设备快绝迹了,所以也可以只添加2x和3x)。
如果仅仅是为了显示更清晰,我们为什么不只用3倍图呢?
只用3倍图,不同手机加载之后也只会去缩小,可以避免因放大而导致的模糊。还会因为引入更少的图片,减少包的大小。
看起来挺好的,也没什么问题。
但是如果只用3倍图,可能会引入如下问题(按照上面例子):
iPhone6加载3倍图时,首先会将3倍图数据加载到内存中,造成了一定的内存损耗,因为大多数情况下,2倍图会比3倍图尺寸小一些。
iPhone6加载300px x 300px 的图片后,图片尺寸会变成 150pt x 150pt,如果显示要求 100pt x 100pt,需要给ImageView设定具体尺寸。而且缩放会增加一些计算量。
iPhone6在将要显示图片的时候,会将图片发送到GPU,GPU会为图片分配计算空间。如果图片大了一些,会消耗过多的GPU计算量。
有一些GPU只允许传入长宽都是2的N次幂尺寸的图片,所以传入GPU前,需要将图片变大为长宽均为2的N次幂。如果图片大了一些,会增加很多计算量,也会急剧增加内存消耗。
如果同时引入3种尺寸的图片,能够避免上述问题。根据经验,多引入的2种尺寸图片,对ipa包体的大小影响极小。
获取当前屏幕px和pt的关系
可以使用 UIScreen.main.scale (OC: [UIScreen mainScreen].scale)来获取 1pt = ?px 的值。
iPhone6中 这个值为2。
iPhone6p中 这个值为3。
比如,我们想要绘制一条高度为1px的线。我们就可以这样写:
//这个 1.0 / UIScreen.main.scale 得到的就是1px对应的pt数值let lineView =UIView(frame:CGRect(x:0, y:100, width:UIScreen.main.bounds.width, height:1.0/UIScreen.main.scale));superView.addSubview(lineView);
绘制图片
如果我们有如下需求,可能就需要自己绘制图片了。
需要对已存在的图片进行修改,比如尺寸修改,添加水印等。
需要根据不同的需求生成图片,比如将富文本变成图片。
需要将当前的某个View输出成图片。
绘制的基本代码如下:
func test(){ let size =CGSize(width:100, height:100) var img =nil;//开启绘制环境//第一个参数 size 表示将要绘制的图片尺寸(单位是pt)。//第二个参数 固定传false,如果置为true,则不会绘制透明区域,透明区域会变成黑色。//第三个参数 表示输出的图片是几倍图。一般设为 UIScreen.main.scale,如果你设置的数值小于 UIScreen.main.scale,绘制出的图片可能会比较模糊(图片的物理尺寸小于size),如果大于UIScreen.main.scale,图片可能会被缩放(图片物理尺寸大于size)。这里物理尺寸指将生成的图片,输出成文件后的图片文件尺寸。//另一个重载函数:UIGraphicsBeginImageContext(CGSize) scale值总是1。UIGraphicsBeginImageContextWithOptions(size,false,UIScreen.main.scale); ...具体绘制工作//从环境中将绘制的图片输出到img中img =UIGraphicsGetImageFromCurrentImageContext();//结束绘制,释放资源UIGraphicsEndImageContext();// img 就是绘制后的图片iflet i = img { let imgData =UIImagePNGRepresentation(i);//若UIGraphicsBeginImageContextWithOptions第三个参数为scale,输出的sava.png的尺寸将为 (size.width x scale, size.height x scale),单位是px。imgData?.write(to: URL.init(fileURLWithPath:"save.png")); }}
细节请看上面注释,另外有2点需要注意。
整个过程所有计算尺寸都是pt。
scale只与输出图片的大小有关,不参与任何尺寸计算。
内容就这么多,下面介绍几个常用的绘制图片的例子:
publicclassTest {//缩放publicstaticfunc resize(srcImg:UIImage, toSize:CGSize) ->UIImage? {UIGraphicsBeginImageContextWithOptions(toSize,false,UIScreen.main.scale); srcImg.draw(in:CGRect(x:0, y:0, width: toSize.width, height: toSize.height)); let img =UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext();returnimg; }//截图publicstaticfunc screenShot() ->UIImage? {iflet windowLayer =UIApplication.shared.keyWindow?.layer {UIGraphicsBeginImageContextWithOptions(UIScreen.main.bounds.size,false,UIScreen.main.scale);iflet ctx =UIGraphicsGetCurrentContext() { windowLayer.render(in: ctx); } let img =UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext();returnimg; }returnnil; }//根据传入颜色生成图片publicstaticfunc createImage(color:UIColor, size:CGSize) ->UIImage? {UIGraphicsBeginImageContextWithOptions(size,false,UIScreen.main.scale);iflet ctx =UIGraphicsGetCurrentContext() { let layer =CALayer.init(); layer.backgroundColor = color.cgColor; layer.bounds =CGRect(x:0, y:0, width: size.width, height: size.height); layer.render(in: ctx); } let img =UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext();returnimg; }//根据富文本生成图片publicstaticfunc createImage(attributedString:NSAttributedString, size:CGSize) ->UIImage? {UIGraphicsBeginImageContextWithOptions(size,false,UIScreen.main.scale); attributedString.draw(in:CGRect(x:0, y:0, width: size.width, height: size.height)); let img =UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext();returnimg; }}
作者:hard_man
链接:https://www.jianshu.com/p/cd72ad707bf3
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。