UIView, CALayer

原文链接: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 对象中获取了绝大多数它所需要的数据。

  1. 每个UIView都有一个层CALayer,控制着各自在屏幕上显示的内容。
    UIView真正的绘图部分,是由一个CALayer类来管理。UIView本身更像是一个CALayer的管理器,访问它的跟绘图和跟坐标有关的属性,例如frame,bounds等,实际上内部都是在访问它所包含的CALayer的相关属性。对比CALayer,UIView多了一个事件处理的功能。CALayer不能处理用户的触摸事件,而UIView可以


  1. UIView内部默认有个CALayer对象(层),通过layer属性可以访问这个层。要注意的是,这个默认的层不允许重新创建,但可以往层里面添加子层
  • UIView可以通过addSubview:方法添加子视图,类似地,CALayer可以通过addSublayer:方法添加子层
  • UIView可以通过subviews属性访问所有的子视图,类似地,CALayer也可以通过sublayers属性访问所有的子层
  • UIView可以通过superview属性访问父视图,类似地,CALayer也可以通过superlayer属性访问父层


  1. 可以通过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);


  1. UIView是定义在UIKit框架中的。CALayer是定义在QuartzCore框架中的。CGImage、CGColor是定义在CoreGraphics框架中的。QuartzCore框架和CoreGraphics框架是可以跨平台使用的,在iOS和macOS上都能使用,但是UIKit只能在iOS中使用。为了保证可移植性,QuartzCore不能使用UIColor,只能使用CGColor。


  1. CALayer的 position、anchorPoint、frame、bounds 属性
  • position和anchorPoint属性都是CGPoint类型的

  • position 是 layer 相对 superLayer 坐标空间的位置,可以用来设置CALayer 在 superLayer 中的位置,它是以父层的左上角为坐标原点(0, 0)

  • anchorPoint称为"定位点",它决定着 CALayer 身上的哪个点会在position 属性所指的位置。它的 x、y 取值范围都是0~1。


    UIView, CALayer_第1张图片

    anchorPoint 默认值为(0.5, 0.5),在这种情况下,CALayer 的中心点会在 position 所指定的位置上,如下图所示:

UIView, CALayer_第2张图片


  • 当初始化UIView时,只设置View的bounds,这种情况下视图的位置如下图:


    UIView, CALayer_第3张图片

    设置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 thebounds, anchorPoint and position properties. When you assign a new value to this property, the layer changes its position and bounds properties to match the rectangle you specified. The values of each coordinate in the rectangle are measured in points.


  1. 当对手动创建的 layer的部分属性进行相应的修改时,默认会自动产生一些动画效果。例如修改layer的bounds、backgroundColor、position是,会产生动画效果,如下图上面是创建的CALayer,下面是创建的UIView,分别改变其位置和高度,对手动创建的 layer会有动画效果:


    UIView, CALayer_第4张图片


  1. UIView的详细显示过程
  • 当UIView需要显示时,它内部的层会准备好一个CGContextRef(图形上下文),然后调用delegate(这里就是UIView)的drawLayer:inContext:方法,并且传入已经准备好的CGContextRef对象。而UIView在drawLayer:inContext:方法中又会调用自己的drawRect:方法

  • 平时在drawRect:中通过UIGraphicsGetCurrentContext()获取的就是由层传入的CGContextRef对象,在drawRect:中完成的所有绘图都会填入层的CGContextRef中,然后被拷贝至屏幕


    UIView, CALayer_第5张图片


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。

UIView, CALayer_第6张图片

我们可以利用 layer 的 mask 属性做些什么呢?
可以设置渐变色的字体,如下图所示:


UIView, CALayer_第7张图片

同样的可以设置渐变色的图片、剪切图片等。
也可以用来实现如 Twitter 的开屏动画效果:


UIView, CALayer_第8张图片
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

你可能感兴趣的:(UIView, CALayer)