原文链接:http://yupeng.fun/2017/12/20/viewlayer/
简介
在iOS中使用UIWindow和UIView在屏幕上显示APP的内容。UIWindow为APP提供了一个底层的容器,用来展示内容,其本身不会显示出来。而UIView可以在UIWindow这个容器里显示出某一部分的内容,你能看得见摸得着的东西基本上都是UIView,比如一个按钮、一个文本标签、一个文本输入框、一个图标等等,这些都是UIView。你可以创建不同的View去显示图片、文字,或者其他组合在一起的视图,你可以使用View去管理、组织其他的View。
在一个应用中,至少要有一个Window,一个View,来去显示应用的内容。在UIKit和其他的一些系统库中,提供了一些定义好的View供你使用,button、label、tableView等等。如果这些不能满足你的需求,也可以自己绘制View、自行获取触控事件。
UIView与CALayer
在iOS中,所有的 view 都是由一个底层的 layer 来驱动的。view 和它的 layer 之间有着紧密的联系,view 其实直接从 layer 对象中获取了绝大多数它所需要的数据。
- 每个UIView都有一个层CALayer,控制着各自在屏幕上显示的内容。
UIView真正的绘图部分,是由一个CALayer类来管理。UIView本身更像是一个CALayer的管理器,访问它的跟绘图和跟坐标有关的属性,例如frame,bounds等,实际上内部都是在访问它所包含的CALayer的相关属性。对比CALayer,UIView多了一个事件处理的功能。CALayer不能处理用户的触摸事件,而UIView可以
- UIView内部默认有个CALayer对象(层),通过layer属性可以访问这个层。要注意的是,这个默认的层不允许重新创建,但可以往层里面添加子层
- UIView可以通过addSubview:方法添加子视图,类似地,CALayer可以通过addSublayer:方法添加子层
- UIView可以通过subviews属性访问所有的子视图,类似地,CALayer也可以通过sublayers属性访问所有的子层
- UIView可以通过superview属性访问父视图,类似地,CALayer也可以通过superlayer属性访问父层
- 可以通过CALayer修改UIView的界面属性。例如设置圆角、阴影、边框、旋转等等。
- 阴影
view.layer.shadowOffset = CGSizeMake(10, 10);
view.layer.shadowColor = [[UIColor cyanColor] CGColor];
view.layer.shadowOpacity = 0.3;
- 旋转
//角度M_PI_4,(x, y, z)维度
view.layer.transform = CATransform3DMakeRotation(M_PI_4, 0, 0, 1);
- UIView是定义在UIKit框架中的。CALayer是定义在QuartzCore框架中的。CGImage、CGColor是定义在CoreGraphics框架中的。QuartzCore框架和CoreGraphics框架是可以跨平台使用的,在iOS和macOS上都能使用,但是UIKit只能在iOS中使用。为了保证可移植性,QuartzCore不能使用UIColor,只能使用CGColor。
- CALayer的 position、anchorPoint、frame、bounds 属性
position和anchorPoint属性都是CGPoint类型的
position 是 layer 相对 superLayer 坐标空间的位置,可以用来设置CALayer 在 superLayer 中的位置,它是以父层的左上角为坐标原点(0, 0)
-
anchorPoint称为"定位点",它决定着 CALayer 身上的哪个点会在position 属性所指的位置。它的 x、y 取值范围都是0~1。
anchorPoint 默认值为(0.5, 0.5),在这种情况下,CALayer 的中心点会在 position 所指定的位置上,如下图所示:
-
当初始化UIView时,只设置View的bounds,这种情况下视图的位置如下图:
设置view的frame、bounds时候,实际上就是在设置view里对应layer的frame、bounds属性。
上面的情况设置 layer 的 bounds 为 (0, 0,80,80),由于没有设置 layer 的 frame 值,系统会根据 layer 的 bounds、anchorPoint、position 的属性值来计算获得 frame 值,anchorPoint默认为(0.5, 0.5) ,position默认值为(0, 0)
frame.origin.x = position.x - anchorPoint.x * bounds.size.width;
frame.origin.y = position.y - anchorPoint.y * bounds.size.height;
所以获取到 frame 值为 (-40, -40, 80, 80),所以就呈现出图中视图的位置,x y 值为负,有一部分已经超出屏幕。
根据上面的计算方法可知,当改变 position、anchorPoint 会改变 frame 的值,改变视图的位置。
position 与 anchorPoint 的改变值互不影响。
The frame rectangle is position and size of the layer specified in the superlayer’s coordinate space. For layers, the frame rectangle is a computed property that is derived from the values in the
bounds
,anchorPoint
andposition
properties. When you assign a new value to this property, the layer changes itsposition
andbounds
properties to match the rectangle you specified. The values of each coordinate in the rectangle are measured in points.
-
当对手动创建的 layer的部分属性进行相应的修改时,默认会自动产生一些动画效果。例如修改layer的bounds、backgroundColor、position是,会产生动画效果,如下图上面是创建的CALayer,下面是创建的UIView,分别改变其位置和高度,对手动创建的 layer会有动画效果:
- UIView的详细显示过程
当UIView需要显示时,它内部的层会准备好一个CGContextRef(图形上下文),然后调用delegate(这里就是UIView)的drawLayer:inContext:方法,并且传入已经准备好的CGContextRef对象。而UIView在drawLayer:inContext:方法中又会调用自己的drawRect:方法
-
平时在drawRect:中通过UIGraphicsGetCurrentContext()获取的就是由层传入的CGContextRef对象,在drawRect:中完成的所有绘图都会填入层的CGContextRef中,然后被拷贝至屏幕
CALayer
1、CALayer 的 mask 属性
/* A layer whose alpha channel is used as a mask to select between the
* layer's background and the result of compositing the layer's
* contents with its filtered background. Defaults to nil. When used as
* a mask the layer's `compositingFilter' and `backgroundFilters'
* properties are ignored. When setting the mask to a new layer, the
* new layer must have a nil superlayer, otherwise the behavior is
* undefined. Nested masks (mask layers with their own masks) are
* unsupported. */
@property(nullable, strong) CALayer *mask;
当设置某个 layer 的 mask 属性之后,只会显示 layer 和 mask 两者重叠的部分,其余部分不会显示,呈现透明。
如下面例子所示,为了方便观察,设置了两个 View 叠在一起,在view2 的 layer 上设置 mask 遮盖,遮盖为椭圆形,设置了之后只显示 view2 和 mask 的重叠部分,即椭圆部分,其余部分没有显示,而且是透明的,显示出了最底层的 view1。
我们可以利用 layer 的 mask 属性做些什么呢?
可以设置渐变色的字体,如下图所示:
同样的可以设置渐变色的图片、剪切图片等。
也可以用来实现如 Twitter 的开屏动画效果:
2、 CALayer addAnimation
当你给一个 CALayer 添加动画的时候,动画其 实并没有改变这个 layer 的实际属性。取而代之的,系统会创建一个原始 layer 的拷贝。在文档中,苹果称这个原始 layer 为 Model Layer ,而这个复制的 layer 则被称为 Presentation Layer 。 Presentation Layer 的属性会随着动画的进度实时改变,而 Model Layer 中对应的属性则并不会改变。所以如果你想要获取动画中每个时刻的状态,请使用 layer 的 func presentationLayer 。
/* Returns a copy of the layer containing all properties as they were
* at the start of the current transaction, with any active animations
* applied. This gives a close approximation to the version of the layer
* that is currently displayed. Returns nil if the layer has not yet
* been committed.
*
* The effect of attempting to modify the returned layer in any way is
* undefined.
*
* The `sublayers', `mask' and `superlayer' properties of the returned
* layer return the presentation versions of these properties. This
* carries through to read-only layer methods. E.g., calling -hitTest:
* on the result of the -presentationLayer will query the presentation
* values of the layer tree. */
- (nullable instancetype)presentationLayer;
/* When called on the result of the -presentationLayer method, returns
* the underlying layer with the current model values. When called on a
* non-presentation layer, returns the receiver. The result of calling
* this method after the transaction that produced the presentation
* layer has completed is undefined. */
- (instancetype)modelLayer;
Reference
View Programming Guide for iOS
http://www.cnblogs.com/mjios/archive/2013/04/14/3020975.html
http://blog.csdn.net/weiwangchao_/article/details/7771538