一般开发中常见的是UIView
或者它的子类,CALayer
似乎并太不常见,最多也就是在倒圆角或者加阴影或者边框的时候想起它来,但是当有一些复杂动画或者不寻常视图显示需求的时候,亦或是界面卡顿束手无策的时候,你会发现CALayer
才是UIView
背后视图显示处理的真正玩家。
iOS开发常用的视图控件都是UIView
的子类,比如:
UILabel -> UIView
UIImageView -> UIView
UITextView -> UIScrollView -> UIView
UIButton -> UIControl -> UIView
UITextField -> UIControl -> UIView
UISegmentedControl -> UIControl -> UIView
UIView
可以处理触摸事件,可以支持基于Core Graphics
绘图,可以做仿射变换(如旋转或者缩放),或者简单的动画像滑动或者渐变。
UIView
是基于CALayer
的封装,目的在于接收触控事件,除此之外像刚提到的简单的动画或者仿射变换其实都是CALayer
的能力,UIView
只不过是通过对它的Layer层赋值和取值来完成这一操作的,每一个UIView
都有一个CALayer
实例图层属性,这个属性会和之后添加视图的实例图层属性行成关系树,用于控制显示图层。
此处也可以留意到,其实UIView
和CALayer
是各司其职的,UIView
负责响应链的处理,CALayer
主要负责视图显示的图层关系和效果的处理,理解这一点,在有一些特殊视图显示和响应链操作需求的时候会有帮助。苹果之所以没有把触控事件和图层处理逻辑集中到一个类处理是因为iOS和Mac OS两个平台的交互事件不一样,如果直接设计两套代码,那关于图层部分其实很多是重复的,大部分区别只在于触控事件不同,所以这样代码封装有利于两个平台共享代码。
- 布局相关的位置和尺寸的映射
UIView
有三个比较重要的布局属性:frame
、bounds
和center
。
CALayer
对应的是:frame
、bounds
和position
。
我们在操作UIView
这三个属性的时候实际上是操作CALayer
对应的三个属性,所以UIView
的三个属性仅仅是存取方法。所以给CALayer
赋能“接收”触控事件是有性能代价的,由此也可知,如果我们在没有事件响应需求的时候,是可以通过直接创建并操作CALayer
级别的图层显示来提升性能的。
frame
属性并不是一个直接属性或者不是一个真实属性,它是由bounds
、position
和transform
计算而来的,所以这三个属性任一个改变都会影响frame
,同样frame
改变也会影响它们。
我们熟知的是frame
是相对于父视图坐标尺寸,bounds
是相对于自己的坐标尺寸。这样我们会觉得它们可能只是相对位置坐标不一样,宽高尺寸是一样的,其实不然。frame
实际上代表了能覆盖图层的整个轴对齐的矩形区域,如果视图旋转的话,那么frame
的宽高和bounds
就不一样了。理解起来有点费劲,来张图就明白了。
-
锚点-anchorPoint
anchorPoint
是CALayer
的属性,并没有对UIView
暴露出来,修改它可以修改图层的位置,但不改变position
。它的取值控件是{0,0}--{1,1}。
-
坐标系
和UIView
身处的二维空间不同,CALayer
身处于一个三维空间中,所以它多了一个垂直于屏幕的z坐标轴,相关的两个属性为CGFloat
类型的zPosition
和anchorPointZ
,一个用于描述图层在z坐标轴的位置,一个用于描述在z坐标轴方向发生几何变化时的参照锚点,默认值都是0,也就是所有视图添加后都在一个平面内。 -
技术小应用
这里我们可以假设一个需求:有两个Button,ButtonA和ButtonB,它们有一部分相交,需求是ButtonA要看起来是在上边显示的(如图),但是点击到相交的区域要ButtonB来响应。
PlanA:可以从事件响应入手,首先判断点击的点的区域是相交的区域,然后拦截父视图的- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
方法返回ButtonB对象,这样就可以完成需求效果,但是两步都比较麻烦。
PlanB:结合前边的内容,我们知道响应链和UIView
有关系,显示层级和CALayer
有关系,现在我们首先要保证响应关系,重叠区域的事件响应是按栈管理来处理的,最后添加的最先响应,那么我们首先保证ButtonB是最后添加的,此时ButtonB在重叠区域是在上边显示的,那么我要做的就是把ButtonA的视图层移动到上边来显示就可以了,这个时候就用到了CALayer
的zPosition
属性了,因为默认都是0,所以把ButtonA视图的CALayer
实例属性的zPosition
属性设置成0.001就OK了,相比PlanA即优雅又简单。
基于CATextLayer封装一个控件,在不需要接收触控事件的时候可以替代UILabel。
demo地址
喜欢就点个赞呗!
欢迎大家提出更好的改进意见和建议,一起进步!
网友评论