Core Animation 文档翻译(第三篇)—设置Layer对象

 


 

前言html

 

当咱们使用核心动画时,Layer对象是一切的核心。Layers 管理咱们APP的可视化content,Layer也提供了content样式及content可视化的外观的调整选项。尽管iOSAPP自动支持Layer,可是OS XAPP必须明确开启Layer的使用才能利用这些相关的性能特色。一旦开启Layer的使用,咱们须要去理解如何配置和操做Layers才能获得想要的效果。后端

 

为APP开启核心动画的支持缓存

 

在iOS APP里,核心动画是一直支持的而且每一个view 都关联一个Layer(这种view被称为layer-backed view)。在OSX APP中,必须手动开启核心动画的支持,关联QuartzCore framework。(iOS APP必须关联这个framework仅当使用核心动画接口时候)。app

开启Layer的支持后,建立layer-backed view是其功能之一,这种View,系统将会负责为其建立Layer对象并保持那个Layer的更新。框架

 

调整和View关联的Layeriview

 

Layer-backed view 默认建立一个CALayer类的实例,大多数状况下咱们不须要其余样式的Layer对象。然而,核心动画提供了其余的Layer类,各个Layer类都具备特殊的功能。选择对应的Layer类有助于咱们提升性能,或者有助于以一种更简单的方式显示特殊样式的content。例如CATiledLayer类为高效的展现大图而优化设计的。ide

 

 改变Uiview的Layer类性能

 

咱们可以改变iOS View 的Layer类别经过重写layerClass方式并一个不一样的类。许多iOS Views建立一个CALayer对象并使用该对象做为content的存储。对于许多咱们本身的Views,默认的layer类别就是一个不错的选择。可是在某些特定的场景别的Layer类是更合适的。例如,咱们可能想要去改变Layer类在一下场景:优化

  ·咱们的View绘制content经过使用Metal 或者OpenGL ES,此时咱们须要使用CAMetalLayer 或者CAEAGLLayer 对象。动画

  ·当有特定的Layer类提供更好的性能。

  ·咱们想要利用某些特定的核心动画Layer类别。例如粒子发射或者粒子复制

改变View的Layer类的方式是很简明的;就像2-1所示同样,咱们须要作的只是重写LayerClass方法而且返回咱们想要使用的类对象在展现以前View将会先调用layerClass方法,并使用它返回的类对象建立一个Layer对象,一旦建立后,该view的layer对象讲不可更换。

+ (Class) layerClass {
   return [CAMetalLayer class];
}

相应的Layer类别的列表,和如何使用它们,参见 Different Layer Classes Provide Specialized Behaviors

 

 各类Layer类提供对应的特性

 

核心动画定义了许多标准的Layer类,每个都具备特定的用途。CALayer是根类,它定义的特性其余全部的Layer都必须支持,CALayer也是默认的类别。固然,咱们也可使用2-1表中的Layer类。

 

Table 2-1  CALayer subclasses and their uses

 

Class

Usage

CAEmitterLayer

实现核心动画中粒子发射系统,发射器Layer控制粒子的生成和他们的原点。

CAGradientLayer

用来画颜色的渐变,以便于用渐变的颜色填充layer上面的形状(通常指绘制的图形)。

CAMetalLayer

用于设置和渲染可绘制的纹理(用于渲染layer 的content经过使用Metal)

CAEAGLLayer/CAOpenGLLayer

用于设置后端存储和渲染layer 的content的上下文(OpenGL ES或者OpenGL )

CAReplicatorLayer

当想要自动拷贝一个或多个子layer时,复制器建立副本,并使用咱们指定的属性改变外观或者副本的属性。

CAScrollLayer

用于管理一个由许多子layer复合而成的可滚动的区域。

CAShapeLayer

用于画一个三维或者二位的贝塞尔曲线。这种layer在绘制基于path形状方面是有利的,由于他们总会建立出一个完善的path;而咱们将path绘制到某个layer的后台存储的,看起来将会有瑕疵当缩放的时候。然而,这个完善的性能牵涉到在主线程渲染该形状并缓存这个结果。(我的以为他应该是保存的矢量图)

CATextLayer

用于渲染文本中普通的或者属性字符串。

CATiledLayer

用于管理可以被分割成许多小块并单独渲染的大图,并支持缩小和放大content.

CATransformLayer

用于渲染真实的3D layer 图层(而不是其余layer类别所展示的平面图层)

QCCompositionLayer

用于渲染一个Quartz Composer 合成品 (OS X only)。

  

为Layer提供contents

 

Layers是管理(由APP提供)content的数据对象。一个Layer的content由包含要被展示的可视化数据的bitmap所组成。咱们能够经过如下三种方式提供bitmap:

  ·直接将一个image对象关联到Layer对象的content属性上。(对于不多或者不会改变的Layer content而言,这是最好的方式。)

  ·为Layer关联一个代理并让这个代理绘制Layer的content。(对于可能按期或者偶尔改变的Layer content,或者要经过某个对象提供contnent,例如view,这是最好的方式。)

  ·定义一个Layer的子类而且重写他的绘制方法以便提供Layer的contents。(若是咱们不得不建立一个Layer的子类,或者若是咱们想要去改变Layer基础的绘制行为,这将是最合适的方法。)

 

当咱们单首创建Layer对象时,也是惟一咱们须要考虑怎么为Layer提供content的时候,若是咱们的APP只包含layer-backed view,咱们没必要考虑前面提到的如何提供Layer content的方法。Layer-backed views将会尽量的以最有效的方式为他们关联的Layer提供contents。

 

 将一个image做为Layer的content

 

由于一个Layer仅仅是一个管理bitmap image的容器,因此咱们直接为Layer的contnent属性关联一个image。将一个图片关联到Layer是很便捷的,而且能让咱们轻松的指定一个图片显示到屏幕上,Layer使用咱们直接提供大的Image,而且将不会建立那个图片的副本。当咱们的APP在多个地方使用同一个图片,这个方式能够节约内存。

咱们关联到Layer的图片必须是一个CGImageRef类型。(在OSX v10.6和以后,咱们也能够关联NSImage对象。)当关联某个图片的时候,记住要提供和设备的分辨率相匹配的图片。对于Retina屏幕的设备而言,咱们须要调整图片的contentsScale属性,更多关于为Layer提供高分辨率contnet能够参见 Working with High-Resolution Images

 

 经过代理为Layer提供content

 

若是咱们的Layer须要动态改变,当须要改变的时候,咱们可使用代理对象来提供或者更新content,在显示的时候,Layer经过调用代理的方法来提供所须要的content。

  ·若是代理实现了displayLayer:方法,那么该方法的实现体须要为建立一个bitmap并将该bitmap关联到Layer的content属性中。

  ·若是代理实现了 drawLayer:inContext:方法,核心动画将会建立bitmap,以及建立用于绘制bitmap的图形context,并调用这个代理方法填充bitmap,此代理方法须要作的就是将所需内容绘制到图形context中。

 

代理对象必须实现displayLayer: 或者drawLayer:incontext:方法,若是代理同时实现了这两个方法,那么代理仅仅只会执行displayLayer:方法。

当咱们的APP须要加载或者建立要显示的bitmap的时候,重写displayLayer:方法是最合适的方法。代码清单2-3展现了displayLayer:代理方法的简单实现过程,在这个例子中,代理对象使用一个协助对象来加载所须要的image。代理方法选择显示哪一个image取决于它自身的状态变量,这个变量在例子中就是这得自定义的属性displayYesImage。

 

Listing 2-3  Setting the layer contents directly

- (void)displayLayer:(CALayer *)theLayer {
    // Check the value of some state property
    if (self.displayYesImage) {
        // Display the Yes image
        theLayer.contents = [someHelperObject loadStateYesImage];
    }
    else {
        // Display the No image
        theLayer.contents = [someHelperObject loadStateNoImage];
    }
}

 

若是咱们没有预先渲染bitmap,或者也没有供建立bitmaps的协助对象,那咱们须要经过drawLayer:incontext:方法实现动态绘制content。代码清单2-4展现了drawLayer:incontext:方法的实现。在这个例子中,代理使用设定的宽度和渲染颜色绘制了一个简单的曲线path。

 

Listing 2-4  Drawing the contents of a layer

- (void)drawLayer:(CALayer *)theLayer inContext:(CGContextRef)theContext {
    CGMutablePathRef thePath = CGPathCreateMutable();
 
    CGPathMoveToPoint(thePath,NULL,15.0f,15.f);
    CGPathAddCurveToPoint(thePath,
                          NULL,
                          15.f,250.0f,
                          295.0f,250.0f,
                          295.0f,15.0f);
 
    CGContextBeginPath(theContext);
    CGContextAddPath(theContext, thePath);
 
    CGContextSetLineWidth(theContext, 5);
    CGContextStrokePath(theContext);
 
    // Release the path
    CFRelease(thePath);
}

 

对于须要自定义content的layer-backed views而言,咱们仍然须要充写view的方法来实现绘制。一个Layer-backed view将他的Layer的代理关联给他自己,并实现了所需的代理方法,并且咱们也不须要手动改变这个配置,不过,咱们须要实现这个layer-backed view的drawRect:方法来绘制咱们的content。

 

 经过子类提供Layer 的content

 

若是咱们须要实现一个自定义的Layer,咱们能够重写Layer的绘制方法作各类各样的绘制。不一样于Layer对象自身生成自定义的content,layer须要管理这个用于显示的content(这句话应该是指的是相对于指定image提供content的方式)。例如,CATiledLayer类管理一个大的image的原理是:经过讲image分隔成许多小的能够单独管理和绘制的片断,因为这个Layer有关于在什么时候须要渲染那一个片断的信息,他直接管理了整个绘制行为。

当自定义子类的时候,咱们须要实现下面两种绘制content的方式之一:

  ·重写子类的display方法,并直接在该方法中设置contents属性。

  ·重写子类的drawInContext:方法而且使用他在该图形context中进行绘制。

选择重写那种方法取决于咱们须要控制绘制过程的程度。display方法是对于更新Layer的content的彻底控制,所以重写该方法将使咱们彻底控制绘制的过程。重写display方法也意味着咱们须要建立CGImageRef并将其关联到content属性。若是咱们仅仅想要绘制content(或者让咱们的layer管理绘制操做),咱们能够重写drawInContext:方法,并让Layer建立后备存储。

 

 对咱们提供的content进行调整

 

当将一个image关联到Layer的content属性时候,Layer的contentGravity属性决定了那个图片将被如何调整为了适应当前的bounds。默认的若是一个图片是比当前的bounds更大或者更小的,Layer对象就会缩放这个图片以便适应可用的空间,若是Layerbounds的宽高比是不一样于image的宽高比,这将会引发图片显示效果不彻底,咱们可使用contentsGravity属性来确保content以最好的方式展示。

可供使用的contentsGravity类型被分为两大种:

  ·基于position的重力常量,使咱们能够在无缩放的状况下,从bounds的某个边或者角铺展image。

  ·基于缩放的重力常量,使咱们能够以某几种方式之一伸缩image,有些选项能够维持宽高比,有些将不维持image的原有宽高比。

图2-1展现了基于点的重力设置对image的影响。除了kCAGravityCenter常量的使用,其余的常量将会以image bounds的某个边或者角来铺展image。kCAGravityCenter常量将image从中心开始铺展。这类常量不会引发图片的伸缩,所以image会按照原来的尺寸进行渲染。若是image是大于Layer的bounds,这将会致使image一部分被裁减掉,若是image的尺寸是小于Layer的bounds,若是设置了layer的背景色,空缺的部分将会显示Layer的背景色。

 

Figure 2-1  Position-based gravity constants for layers

 

图2-2展现了基于缩放的重力常量如何影响image显示的。若是image的尺寸和Layer的bounds不一致,全部的这些常量将会缩放image。这些常量的不一样之处在于如何调整这些图片原来的宽高比,其中有些模式是保持原来的宽高比,还有一些将会改变原来的宽高比。默认状况下Layer的contentsGravity属性是被设置为kCAGravityResize 常量,这是该类型中惟一不保持图片原有宽高比的图片。

 

  Figure 2-2  Scaling-based gravity constants for layers

 

 如何使用高分辨率的Images

 

Layers自己并不知道所在设备屏幕分辨率。Layer仅仅存储指向bitmap的指针并使用所给定的可利用的像素按照尽量好的方式展示。若是咱们将一个Image关联到一个Layer的content属性,咱们必须经过设置Layer的contentScale属性告诉核心动画Image的分辨率。ContentScale属性的默认值是1.0,这只适用于那些将要显示到标准分辨率屏幕的Image,若是咱们的Image想要显示到Retina屏幕上,那么咱们就必须设置这个属性为2.0。

 

若是咱们直接关联一个bitmap到Layer,那么咱们就须要设置contentScale的属性。为了适配屏幕的分辨率和被View管理的content,UIKit 和AppKit框架中 layer-backed view会自动设置Layer的缩放因子。例如,在OSX中,若是咱们关联一个NSImage对象到Layer的contnet属性,AppKit将会查找标准和更高分辨率的Image,若是都有,那么AppKit将会使用正确的分辨率的image来设置contentScal的属性

 

 调整Layer的可视化风格和展现样式

 

Layer对象能够建立可视化的配件来增添Layer的主要contents,例如边框和背景色。这些可视化的配件不须要咱们本身作任何的渲染工做,所以在某些状况下就可使用Layer做为单独的总体。全部须要咱们作的仅仅是设置Layer的属性,Layer将会处理所需的绘制工做,固然也包含任何动画。对于可视化配件是如何影响Layer显示的说明能够参见 Layer Style Property Animations

 

 Layer有他们本身的背景和边框

 

除了他的基于image的content以外,一个Layer能够显示填充的背景和填充的边框。背景色是在Layer的content后面渲染的,边框是在image的content的上面渲染的,就像图2-3所示。若是Layer包含子Layer,他们也出如今边框的下面。因为背景色是放置在image的后面的,image的透明的部分将会显示背景色。

 

Figure 2-3  Adding a border and background to a layer

 

 

 

代码清单2-5显示设置Layer的背景色和边框所须要的代码,全部这些属性都是可动画的。

myLayer.backgroundColor = [NSColor greenColor].CGColor;
myLayer.borderColor = [NSColor blackColor].CGColor;
myLayer.borderWidth = 3.0;

注意:咱们可使用任何颜色做为Layer的背景色,包括带有透明份量的颜色或者使用样品image。当使用样品image的时候,要注意Core Graphics 处理图片的渲染,以及在此处理图片渲染的过程当中将使用标准坐标系统,标准坐标系统和iOS中默认的坐标系统是不一样的。在iOS上默认状况下,图片渲染是上下颠倒的,除非咱们调整坐标系统。

 

若是咱们设置Layer的背景色为不透明的颜色,那么咱们应该讲Layer的opaque属性设置为YES,这么作将会提高性能当合成该Layer到屏幕上显示时候,并消除了layer 做为辅助存储时候管理的透明通道。若是Layer有非0的圆角,咱们就没必要将该Layer标记为不透明。

 

 Layer支持圆角

 

经过添加corner radius,咱们能够为Layer建立圆角效应。corner radius是一个可视化配件,它能够遮盖Layer的四角让下面的content显示出来。就像图2-4显示的。它涉及到透明遮罩的应用,corner radius不影响image的Layer中的content属性,除非设置masksToBounds属性为YES。然而,corner radiu 一直影响Layer的背景色和边框的绘制。

 

Figure 2-4  A corner radius on a layer

 

 

为了将corner radius应用到Layer,咱们须要为Layer的cornerRadius属性指定一个值。圆角的指定单位是points,而且显示的时候,它将会预先应用到Layer的四个角。

 

 Layer支持内建阴影

 

CALayer 类包含几个为配置阴影效应的属性。阴影会经过让它看起来像是漂浮在content下面的方式来为Layer添加深度。这是另一种可视化的配件,当APP须要的时候可使用。对于Layer而言咱们能够控制阴影的颜色,相对于content的位置,不透明度,和形状。

Layer阴影的不透明度默认被设置为0,这将会有效的隐藏阴影。改变一个透明度为非零的值,将会引发核心动画绘制阴影。阴影默认是被直接放置在Layer的下面的,为了可以看到阴影,咱们也须要调整阴影的偏移量。有个很重要的事情须要记住,咱们为阴影指定的偏移量是基于Layer的本地坐标系统的,也就是说在iOS和OSX 上面是不一样的。如图2-5展现了一个偏向右下的阴影,在iOS中,须要指定一个正数在Y轴份量上,可是在OSX 上就须要指定为负值。

 

Figure 2-5  Applying a shadow to a layer

 

当为Layer添加阴影的时候,阴影就是Layer的content的一部分,可是实际上阴影有可能超出Layer的边界,若是开启Layer的maskToBounds属性那么阴影的效应将会在Layer的边界处被裁剪,这将会产生一个奇怪的现象,那就是在Layer的内部的阴影是显示的,可是在Layer的边界外部的阴影缺看不到。若是咱们想要显示完整的阴影而且还想使用bounds masking,咱们就应该使用两个Layer而不是一个,将mask应用到包含content的Layer上,而后将这个Layer嵌入到第二个Layer上,第二个Layer的尺寸和第一个的尺寸同样,第二个Layer上设定阴影效果。

更多关于如何Layer上如使用阴影可参见Shadow Properties

 

 为Layer添加自定义属性

 

CAAnimation 和CALayer 类延伸了KVC以便支持自定义属性。咱们能够其使用这个特性为Layer添加数据,而后经过自定义的属性获取该数据。咱们甚至能够关联actions到自定义的属性中,进而达到当修改这个属性的时候,对应的动画将会被执行。

为更多的信息关于如何使用和设置自定义属性,能够参见 Key-Value Coding Compliant Container Classes。为更多的信息关于添加actions到Layer对象,能够参见 Changing a Layer’s Default Behavior

 

 打印Layer-backed View的内容

 

在打印期间,Layer 会重绘他们的contents为了适配打印环境。核心动画正常状况下依靠缓存的bitmap当渲染到屏幕上的时候,然而他将会重绘制content当打印的时候。尤为,layer-backed view使用drawRect:方法提供Layer 的content,当打印的时候,核心动画将再次调用drawRect:来生成须要打印的Layer的content。

相关文章
相关标签/搜索