iOS 视图在层级关系中相互嵌套,一个视图可以管理它的所有子视图的位置。
CALayer类在概念上和UIView类似,同样也是一些被层级关系树管理的矩形块,同样也可以包含一些内容(像图片,文本或者背景色),管理子图层的位置。它们有一些方法和属性用来做动画和变换。和UIView最大的不同是CALayer不处理用户的交互。CALayer并不清楚具体的 响应链 (iOS通过视图层级关系用来传送触摸事件的机制),于是它并不能够响应事件。
每个UIVIew都有一个CALayer实例的图层属性,视图的职责就是创建并管理这个图层,以确保当子视图在层级关系中添加或者被移除的时候,他们关联的图层也同样对应在层级关系树当中有相同的 *** 作。
虽然苹果已经通过UIView的高级API进行动画的管理并且使得动画变得简单易用。CALayer功能更为灵活:
CALayer 有一个属性叫做contents,这个属性的类型被定义为id。如果你给contents赋的不是CGImage,那么你得到的图层将是空白的。
contents这个奇怪的表现是由Mac OS的历史原因造成的。它之所以被定义为id类型,是因为在Mac OS系统上,这个属性对CGImage和NSImage类型的值都起作用。如果你试图在iOS平台上将UIImage的值赋给它,只能得到一个空白的图层。一些初识Core Animation的iOS开发者可能会对这个感到困惑。
你真正要赋值的类型应该是CGImageRef,它是一个指向CGImage结构的指针。UIImage有一个CGImage属性,它返回一个"CGImageRef",如果你想把这个值直接赋值给CALayer的contents
,那你将会得到一个编译错误。因为CGImageRef并不是一个真正的Cocoa对象,而是一个Core Foundation类型。
尽管Core Foundation类型跟Cocoa对象在运行时貌似很像(被称作toll-free bridging),他们并不是类型兼容的,不过你可以通过bridged关键字转换。如果要给图层的寄宿图赋值,你可以按照以下这个方法:
contentsGravity与UIView中的contentsMode类似,UIVIewImage中可以通过设置contentMode 属性,使得添加的图片更适合。
UIView大多数视觉相关的属性比如contentMode
,对这些属性的 *** 作其实是对对应图层的 *** 作。
CALayer与contentMode对应的属性叫做contentsGravity,但是它是一个NSString类型,而不是像对应的UIKit部分,那里面的值是枚举。contentsGravity可选的常量值有以下一些:
contentsScale属性定义了寄宿图的像素尺寸和视图大小的比例,默认情况下它是一个值为1.0的浮点数。
如果你只是单纯地想放大图层的contents
图片,你可以通过使用图层的transform
和affineTransform属性来达到这个目的。
contentsScale属性其实属于支持高分辨率(又称Hi-DPI或Retina)屏幕机制的一部分。它用来判断在绘制图层的时候应该为寄宿图创建的空间大小,和需要显示的图片的拉伸度(假设并没有设置contentsGravity属性)。
如果contentsScale 设置为1.0,将会以每个点1个像素绘制图片,如果设置为2.0,则会以每个点2个像素绘制图片,这就是我们熟知的Retina屏幕。
这并不会对我们在使用kCAGravityResizeAspect时产生任何影响,因为它就是拉伸图片以适应图层而已,根本不会考虑到分辨率问题。但是如果我们把contentsGravity设置为kCAGravityCenter(这个值并不会拉伸图片),那将会有很明显的变化
注意:
当我们对Layer添加图片时设置contentsGravity为kCAGravityResizeAspect,此时设置contentsScale不会对图片显示有任何影响。但是当我们把contentsGravity设置成kCAGravityCenter,这时就需要通过手动设置contentsScale修复图片不正常显示问题;
和UIImage不同,CGImage没有拉伸的概念。当我们使用UIImage类去读取我们的雪人图片的时候,他读取了高质量的Retina版本的图片。但是当我们用CGImage来设置我们的图层的内容时,拉伸这个因素在转换的时候就丢失了。不过我们可以通过手动设置contentsScale来修复这个问题。
当我们在Layer中添加图片,但图片超出Layer范围时,会有显示不全的问题。但不管是UIView还是Layer都可以对超出范围的内容或子视图进行绘制。
UIView中用的是clipsToBounds 来确定是否显示超出的内容是否显示,CALayer中的则是 masksToBounds ,当把其设置为YES时,即显示界外内容。
iOS中View和layer的区别:
1、View负责处理事务,layer用于显示.
2、layer是CALayer的类型,其创建方式和UIView创建方式类似。不过通过此方式添加的图层在模拟器的层次结构中只能看到一个view。其相当于在view上染了一个宽和高分别为80的湖蓝色,而不是在view上添加了一个宽和高为80的view。
layer用法:
1、CALayer *layer = [[CALayer alloc] init]
2、layer.bounds = CGRectMake(100,100,80,80) //设置其大小
3、layer.backgroundColor = [UIColor cyanColor].CGColor //设置其背景色
4、layer.position = CGPointMake(200,200) //设置其位置
5、[self.view.layer addSublayer:layer] //将其添加到根图层上
1、数据处理过程
2、硬件协同处理细节
1、从文件加载图片数据 可以使用类方法 +imageNamed: ,此时我们得到图片原始数据
2、将 UIImage 赋值给 UIImageView
3、 CATransaction 捕获到 UIImageView 图层树的变化
4、主线程runloop提交 CATransaction,开始进行解码和图像渲染,这里会涉及到
5、渲染
注意:
1、图片只有在确认需要显示时,CPU才对其进行解压+imageName: or +imageNamed:inBundle:compatibleWithTraitCollection: 加载的图片,系统会对其自动缓存,以便下次使用,但如果用 +imageWithContentsOfFile: 、 -initWithContentsOfFile: 系统会每次都从disk加载,对应的都需要解压; imageWithData 直接读取原始数据,如果非GPU能直接处理的数据(位图或矢量图)是需要解压的;
2、解码是一个比较消耗CPU的 *** 作,且默认在主线程,当界面上一次性确定显示多张图片时表现得尤为突出,此时一般的做法可以将解码 *** 作放到异步线程去完成,解码后再用来展示
1、YYImage 类结构分析
2、分析YYAnimatedImageView
初始化中指定了 runloopMode 为 NSRunLoopCommonModes
_runloopMode 指定到了 CADisplayLink 的 link 中,不断地替换 _curFrame 用来跟新 layer 里面显示的图片 frame ,将 link 的指定成 NSRunLoopCommonModes 保证了在拖动滚动视图时动画还能继续。
可以看到, _YYAnimatedImageViewFetchOperation 解码image图片的 *** 作是通过
NSOperationQueue 的 _requestQueue 来异步来处理的
这里cache了显示所需要解压好的图片buffer数据,以便重复使用当然这里还有缓存限制和memory告警时清理缓存的机制,我这里就不一一赘述了
参考链接:
https://developer.apple.com/documentation/uikit/uiimage
https://www.jianshu.com/p/72dd074728d8
https://developer.apple.com/documentation/uikit/uiimage
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)