Core Animation - 6

来源:互联网 发布:淘宝卖家从哪里登陆 编辑:程序博客网 时间:2024/06/05 14:36

Layer Trees Reflect Different Aspects of the Animation State

An app using Core Animation has three sets of layer objects. Each set of layer objects has a different role in making the content of your app appear onscreen:

  1. Objects in the model layer tree (or simply “layer tree”) are the ones your app interacts with the most. The objects in this tree are the model objects that store the target values for any animations. Whenever you change the property of a layer, you use one of these objects.
  2. Objects in the presentation tree contain the in-flight values for any running animations. Whereas the layer tree objects contain the target values for an animation, the objects in the presentation tree reflect the current values as they appear onscreen. You should never modify the objects in this tree. Instead, you use these objects to read current animation values, perhaps to create a new animation starting at those values.
  3. Objects in the render tree perform the actual animations and are private to Core Animation.

Each set of layer objects is organized into a hierarchical structure like the views in your app. In fact, for an app that enables layers for all of its views, the initial structure of each tree matches the structure of the view hierarchy exactly. However, an app can add additional layer objects—that is, layers not associated with a view—into the layer hierarchy as needed. You might do this in situations to optimize your app’s performance for content that does not require all the overhead of a view. Figure 1-9 shows the breakdown of layers found in a simple iOS app. The window in the example contains a content view, which itself contains a button view and two standalone layer objects. Each view has a corresponding layer object that forms part of the layer hierarchy.

官方文档指出了三个层树,分别是: model layer tree , presentation tree , render tree 。 模型树,呈现树,渲染树。
如图:


由上图我们可以看到Layer Tree 和 Presentation Tree 是一一对应的,而渲染树则是整一棵私有的。私有的渲染树是系统控制管理和负责的。然而Layer Tree总是反应着当前的最后层的动画状态,而Presentation Tree 则是代表着执行动画的状态的层的state。这也说明白了为什么Layer Trees Reflect Differenct Aspects of the Animation state。



The Relationship Between Layers and Views

这另开一贴



Changing the Layer Object Associated with a View

Layer-backed views create an instance of the CALayer class by default, and in most cases you might not need a different type of layer object. However, Core Animation provides different layer classes, each of which provides specialized capabilities that you might find useful. Choosing a different layer class might enable you to improve performance or support a specific type of content in a simple way. For example, theCATiledLayer class is optimized for displaying large images in an efficient manner.
一个基于层的视图(其实就是普通的视图罢了)会默认地创建一个CALayer类,而且大多数情况下,我们都不用操心一个与之不同的层对象(很明显入门开发也很少遇到层对象的使用)。但是如果要使用Core Animation,你必须使用一个不同的层对象(前面的学习应该知道了Layer-backed的默认层是一个主层,是作为inherit Tree的根容器),然为为什么要使用不同的层对象,因为层对象暴力地提高了性能和不同于UIView的不同的、更明确的操作。
例如,CATiledLayer 类来优化展示大尺寸的图像用一种更高效的方式。



Changing the Layer Class Used by UIView

You can change the type of layer used by an iOS view by overriding the view’s layerClass method and returning a different class object. Most iOS views create a CALayer object and use that layer as the backing store for its content. For most of your own views, this default choice is a good one and you should not need to change it. But you might find that a different layer class is more appropriate in certain situations. For example, you might want to change the layer class in the following situations:

这里提到了一个方法:
+ (Class)layerClass;                        // default is [CALayer class]. Used when creating the underlying layer for the view.
默认返回[CALayer class],你可以修改层的类型使用重载这个方法并且会相应地返回一个不同的类对象。但大多数的视图对象是不会去修改的,注意这个underlying,使用这个方法所修改的层对象就是层继承树的根部层容器,你可以修改默认层由于以下的几种情况:
  • Your view draws content using Metal or OpenGL ES, in which case you would use a CAMetalLayer or CAEAGLLayer object.
  • There is a specialized layer class that offers better performance.
  • You want to take advantage of some specialized Core Animation layer classes, such as particle emitters or replicators.
当使用了Metal 或者 OpenGL ES,这种情况下你会使用:CAMetalLayer 或者 CAEAGLLayer 对象。
如果你修改以后有了更好的性能。
使用了特定的Core Animation 层类。

 

Changing the layer class of a view is very straightforward; an example is shown in Listing 2-1. All you have to do is override thelayerClass method and return the class object you want to use instead. Prior to display, the view calls thelayerClass method and uses the returned class to create a new layer object for itself. Once created, a view’s layer object cannot be changed.

修改层类非常简单,并且在视图显示之前会执行这个方法,一旦创建,无法改变哦
 /** 返回一个CAMetalLayer类 **/ /** 把默认的底层层类修改了   **/ + (Class) layerClass  {  return [CAMetalLayer class]; }
类似操作的类有一下那么多:
CAEmitterLayer , CAGradientLayer , CAMetalLayer , CAEAGLLayer/CAOpenGLLayer , CAReplicatorLayer , 
CAScrollLayer , CAShapeLayer , CATextLayer , CATiledLayer , CATransformLayer , QCCompositionLayer
具体的不同和操作可以参看文档。关于上面的Metal可以自定了解一下,是非常炫酷的东西哦


Providing a Layer’s Contents

 Layers are data objects that manage content provided by your app. A layer’s content consists of a bitmap containing the visual data you want to display. You can provide the content for that bitmap in one of three ways:
  • Assign an image object directly to the layer object’s contents property. (This technique is best for layer content that never, or rarely, changes.)
  • Assign a delegate object to the layer and let the delegate draw the layer’s content. (This technique is best for layer content that might change periodically and can be provided by an external object, such as a view.)
  • Define a layer subclass and override one of its drawing methods to provide the layer contents yourself. (This technique is appropriate if you have to create a custom layer subclass anyway or if you want to change the fundamental drawing behavior of the layer.)
层是一个数据对象管理应用的内容。一个层的内容由位图组成包含了你想要展示的可视化的数据,可以使用三种发式来提供内容的位图:
1.直接赋予层对象的contents 属性(这种方法对于从来不或者很少修改层对象的情况下)
/** Layer content properties and methods. **//* An object providing the contents of the layer, typically a CGImageRef, * but may be something else. (For example, NSImage objects are * supported on Mac OS X 10.6 and later.) Default value is nil. * Animatable. */@property(strong) id contents;
注意到typically a CGImageRef 常常这个属性就是默认地为这个类型,当然了出了一些特俗情况,例如这里说的,可以为nil

2.分配委托对象给层并且使用这个委托来重绘层的内容(这种技术对于定期修改层内容并且能够被外部对象提供,例如视图)
相应的例子在下面Using a Delegate to Provide the Layer's Content 里面

3.定义个层的子类并且重载它部分或全部的绘图方法来自身地提供层对象(这种方法对于你想要创建一个自定义的子层对象或者你想要修改层的基本绘图方法)
The only time you need to worry about providing content for a layer is when you create the layer object yourself. If your app contains nothing but layer-backed views, you do not have to worry about using any of the preceding techniques to provide layer content. Layer-backed views automatically provide the contents for their associated layers in the most efficient way possible.


Using an Image for the Layer’s Content

Because a layer is just a container for managing a bitmap image,you can assign an image directly to the layer’s contents property. Assigning an image to the layer is easy and lets you specify the exact image you want to display onscreen. The layer uses the image object you provide directly and does not attempt to create its own copy of that image. This behavior can save memory in cases where your app uses the same image in multiple places.

The image you assign to a layer must be a CGImageRef type. (In OS X v10.6 and later, you can also assign an NSImage object.) When assigning images, remember to provide an image whose resolution matches the resolution of the native device. For devices with Retina displays, this might also require you to adjust the contentsScale property of the image. For information on using high-resolution content with your layers, see Working with High-Resolution Images.

这里对应着上面第一种方法直接赋予层的contents属性,常用的就有CGImageRef这种类型。


Using a Delegate to Provide the Layer’s Content

If the content of your layer changes dynamically, you can use a delegate object to provide and update that content when needed. At display time, the layer calls the methods of your delegate to provide the needed content:

  • If your delegate implements the displayLayer: method, that implementation is responsible for creating a bitmap and assigning it to the layer’s contents property.
  • If your delegate implements the drawLayer:inContext: method, Core Animation creates a bitmap, creates a graphics context to draw into that bitmap, and then calls your delegate method to fill the bitmap. All your delegate method has to do is draw into the provided graphics context.
如果层对象的内容是动态地变化,就可以使用委托对象来提供和更新层的内容。当页面展示的时候,层调用委托的方法来提供相应所需的内容:
1.如果委托对象实现了displayLayer: 方法,这个实现会对创建位图并且将之分配给层的contents属性所负责
2.如果委托对象实现了drawLayer:inContext:方法,Core Animation 创建一个位图,创建一个图形上下文来绘制位图,并且调用你的委托方法来填充这个位图。你的委托方法所要做的就是在所提供的图形上下文里绘制。

The delegate object must implement either the displayLayer: ordrawLayer:inContext: method. If the delegate implements both thedisplayLayer: and drawLayer:inContext:method, the layer calls only thedisplayLayer: method.

委托对象必须实现上面提及的两个方法中的其中一种,但是如果两个都实现了,只会调用diaplayLayer:方法。

Overriding the displayLayer:method is most appropriate for situations when your app prefers to load or create the bitmaps it wants to display. Listing 2-3 shows a sample implementation of thedisplayLayer: delegate method. In this example, the delegate uses a helper object to load and display the image it needs. The delegate method selects which image to display based on its own internal state, which in the example is a custom property calleddisplayYesImage.
重载displayLayer:最适合在当你的应用更倾向于家在或者创建所要展示的位图的时候。  

例子如下:
- (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];    }}

If you do not have prerendered images or a helper object to create bitmaps for you, your delegate can draw the content dynamically using thedrawLayer:inContext: method. Listing 2-4 shows a sample implementation of thedrawLayer:inContext: method. In this example, the delegate draws a simple curved path using a fixed width and the current rendering color.

如果没有预渲染的图片或者帮助对象来创建位图的话,你的委托可以动态地绘制图层的内容通过方法drawLayer:inContext:。在这个例子中,委托方法绘制了一个简单的曲线路径使用了一个固定的宽度和当前已经渲染的颜色。
- (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);}
仔细对比相应的方法就会知道两者的实现的用途和区别了。

For layer-backed views with custom content, you should continue to override the view’s methods to do your drawing. A layer-backed view automatically makes itself the delegate of its layer and implements the needed delegate methods, and you should not change that configuration. Instead, you should implement your view’sdrawRect: method to draw your content.

对于layer-backed视图自定义的内容,你应该重载视图的方法来完成绘制。一个layer-backed视图自动地把它本身作为它的层属性的委托并且实现了一些重要的方法,你不能修改原有的配置。而相应地,你应该重载你的drawRect:方法来绘制你的视图内容
关于这个,这个有一片非常好的文章:http://www.cocoachina.com/ios/20141022/10005.html


Providing Layer Content Through Subclassing

If you are implementing a custom layer class anyway, you can override the drawing methods of your layer class to do any drawing. It is uncommon for a layer object to generate custom content itself, but layers certainly can manage the display of content. For example, the CATiledLayer class manages a large image by breaking it into smaller tiles that can be managed and rendered individually.Because only the layer has information about which tiles need to be rendered at any given time, it manages the drawing behavior directly.

CATiledLayer类管理大尺寸图像通过将它分成一片的从而单独渲染(怪不得叫做tiled 砖瓦式地单独处理),因为仅仅层有关于每一处在给定的时刻需要被渲染的,它直接地对绘制行为操作。

When subclassing, you can use either of the following techniques to draw your layer’s content:

  • Override the layer’sdisplay method and use it to set thecontents property of the layer directly.
  • Override the layer’sdrawInContext: method and use it to draw into the provided graphics context.
继承子类,这里提到了两个重要的图层方法: displaydrawInContext:
/* Reload the content of this layer. Calls the -drawInContext: method * then updates the `contents' property of the layer. Typically this is * not called directly. */- (void)display;

/* Called via the -display method when the `contents' property is being * updated. Default implementation does nothing. The context may be * clipped to protect valid layer content. Subclasses that wish to find * the actual region to draw can call CGContextGetClipBoundingBox(). */- (void)drawInContext:(CGContextRef)ctx;

Which method you override depends on how much control you need over the drawing process. Thedisplay method is the main entry point for updating the layer’s contents, so overriding that method puts you in complete control of the process. Overriding thedisplay method also means that you are responsible for creating theCGImageRef to be assigned to thecontents property. If you just want to draw content (or have your layer manage the drawing operation), you can override thedrawInContext:method instead and let the layer create the backing store for you.

大概解释的就是display方法是更新图层内容的主要入口,重载display方法也意味着你要为创建一个CGImageRef来赋值给contents属性。如果仅仅是为了绘制内容(或者是拥有图层管理绘制操作的使用权),你可以重载drawInContext这个方法,让图层创建backing store。


Tweaking the Content You Provide

When you assign an image to thecontentsproperty of a layer, the layer’scontentsGravity property determines how that image is manipulated to fit the current bounds. By default, if an image is bigger or smaller than the current bounds, the layer object scales the image to fit within the available space. If the aspect ratio of the layer’s bounds is different than the aspect ratio of the image, this can cause the image to be distorted. You can use thecontentsGravity property to ensure that your content is presented in the best way possible.

当你分配一个图像(通常就是CGImageRef类型)给图层的contents属性时,图层的contentsGravity属性会决定了这个图像如何来适配当前的bounds(说到这里就不得不想起一篇非常的博客:关于bounds,anchorpoint,position,transform 之间很好的总结:http://blog.sina.com.cn/s/blog_8f5097be0101b91z.html),默认的,如果图像的大小比当前的bounds大或者小,图层对象将会将该图像调整成一个适当的可利用大小的空间(因为contentsGravity默认地使用:kCAGravityResize)。如果图层的宽高比与图像的宽高比不相同,这将会使得图像被扭曲。你可以使用contentsGravity属性来保证你的图像处于最好的状态。

The values you can assign to thecontentsGravity property are divided into two categories:

有两种类型的属性值:
  • Theposition-based gravity constantsallow you to pin your image to a particular edge or corner of the layer’s bounds rectangle without scaling the image.
  • The scaling-based gravity constants allow you to stretch the image using one of several options, some of which preserve the aspect ratio and some of which do not.
/** Layer `contentsGravity' values. **/CA_EXTERN NSString * const kCAGravityCenter    __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);CA_EXTERN NSString * const kCAGravityTop    __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);CA_EXTERN NSString * const kCAGravityBottom    __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);CA_EXTERN NSString * const kCAGravityLeft    __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);CA_EXTERN NSString * const kCAGravityRight    __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);CA_EXTERN NSString * const kCAGravityTopLeft    __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);CA_EXTERN NSString * const kCAGravityTopRight    __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);CA_EXTERN NSString * const kCAGravityBottomLeft    __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);CA_EXTERN NSString * const kCAGravityBottomRight    __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);CA_EXTERN NSString * const kCAGravityResize    __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);CA_EXTERN NSString * const kCAGravityResizeAspect    __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);CA_EXTERN NSString * const kCAGravityResizeAspectFill    __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);
关于contentsGravity的常亮属性值如上:
Position-based gravity 和 Scaling-based gravity 分别如下面两张图所示:

基于位置的设置会以坐标式的布局去安排子层在父层的展示位置,然而基于调整的设置会根据设置值相应地保留、拉伸图像内容,上面两图很好理解吧!








0 0
原创粉丝点击