[译] Core Animation 编程指南 - 设置图层对象

1,076

本文首发地址

图层对象是你用 Core Animation 所做的一切事情的核心。图层管理你 app 的视觉内容,提供视觉内容样式和视觉外观的修改选项。虽然 iOS app 内嵌自动支持图层,但是 OS X 上面的 app 开发者必须在利用图层的性能优势前手动让图层生效。一旦生效,你需要理解如何配置和操作你 app 的图层,来获取你想要的效果。

让你的 App 支持 Core Animation

在 iOS 的 app 中, Core Animation 总是自动生效的,并且每个视图是由图层支持的。在 OS X 中,app 必须通过下面的操作来使 Core Animation 生效:

  • 与 QuartCore 框架链接(iOS app 只有在明确使用 Core Animation 的时候才必须与该框架链接)
  • 通过以下操作来使你的 NSView 对象支持图层:
    • 在你的 nib 文件,使用 View Effects 检查器来使你的视图支持图层。该检查器显示选中视图及子视图的复选框。推荐你尽可能的将你的视图修改为图层支持的视图。
    • 如果你用代码创建的视图,调用视图的 setWantsLayer: 方法传递 YES 来说明该视图应该使用图层。
  • 通过上面的任意一种方法都可以创建图层支持的视图。使用图层支持的视图,系统会管理创建底层的图层对象,保持图层的更新。在 OS X 上面,你也可以创建图层托管视图,由此,你的 app 实际会创建和管理底层的图层对象。(你不能在 iOS 上面创建图层托管视图)关于如何创建图层托管视图的更多信息,可参见 图层托管允许你改变 OS X 上的图层对象

改变视图关联的图层对象

图层支持的视图默认会创建 CALayer 的实例,多数情况下你不需要去改变图层对象的类型。但,Core Animation 提供了一些不同的图层类,每一个提供的图层类都是很有用处的。选择不同的图层类可能会提高你的代码性能,或者通过简单的方式支持一种指定的内容类型。例如, CATiledLayer 类通过高效的方式用来优化大型图片的显示。

改变视图使用的图层类

你可以通过重写视图的 layerClass 方法返回一个不同的类对象,来改变 iOS 上视图使用的图层类。大多数的 iOS 视图创建一个 CALayer 对象,并使用该对象支持存储它的内容。对于你的大多数视图,默认的选择是不错的,你不需要去改变它。但是在某些情况下,你会发现一个不同的图层类是更加合适的。例如,以下几种情况你可能想改变图层类:

  • 你的视图使用 Metal 或者 OpenGL ES 来绘制内容,这种情况下,你应该使用 CAMetalLayer 或者 CAEAGLLayer 对象。
  • 有特定类型的图层对象可以提供更好的性能。
  • 你想使用一些特定的 Core Animation 图层类,例如粒子发射器(particle emitters)或复制器(replicators)。

改变视图的图层类是非常简单的;可以参见 例2-1 的例子。你需要做的就是重写 layerClass 方法,返回一个你想使用的类对象。显示之前,视图会调用 layerClass 方法,使用返回的类创建一个新的图层对象。视图的图层对象一旦被创建,就不可修改。

例 2-1 改变视图的图层类

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

// Swift
override class var layerClass: AnyClass {
    return CAMetalLayer.self
}

关于图层类的列表和你如何使用它们,可参见不同的图层类提供专门的行为

改变 NSView 的图层类

你可以通过重写 NSView 对象的 makeBackingLayer 方法来改变它的默认图层类。在该方法里,创建、返回一个你想 AppKit 去用来支持你自定义视图的图层对象。你可能以下情况需要重写该方法:使用类似滚动图层(scrolling)或者平铺图层(tiled layer)的自定义图层。关于图层类的列表和你如何使用它们,可参见不同的层类提供专门的行为

图层托管允许你改变 OS X 上的图层对象

图层托管视图:你自己创建、管理底层的图层的 NSView 对象。如果你想自己控制视图的图层类型,你需要使用图层托管。例如,你可以创建一个图层托管视图,这样你可以分配一个不同的图层类,而不是默认的 CALayer 。你想使用一个单独的视图去管理独立图层的层级结构时,也可以创建图层托管视图。

当你的视图调用 setLayer: 方法并且提供了一个图层对象时,AppKit 将不会管理该图层对象。通常,AppKit 会更新视图的图层对象,但对于图层托管的视图,大多数属性不会更新。

为了创建一个图层托管视图,你需要创建自己的图层对象,并在视图在屏幕显示之前将图层和视图关联起来,如例 2-2 中代码所示。为了设置图层对象,你必须调用 setWantsLayer: 方法让视图知道它应该使用图层。

例 2-2 创建一个图层托管视图

// Create myView...

[myView setWantsLayer:YES];
CATiledLayer *hostedLayer = [CATiledLayer layer];
[myView setLayer:hostedLayer];

// Add myView to a view hierarchy.

如果你选择使用图层托管视图,你必须自己设置 contentsScale 属性,并且在合适的时机提供高分辨率的内容。关于高分辨率内容和倍率的更多内容,请参见使用高分辨率图像

不同的图层类提供专门的行为

Core Animation 定义了很多标准图层类,这些类都是为某种特定的情况定义使用的。CALayer 是所有图层类对象的父类。它定义了所有图层对象必须支持的行为,且它是图层支持视图默认的图层类型。其它具体图层类可参见表 2-1。

2-1 CALayer 的子类和它们的使用

类名 具体用途
CAEmitterLayer 用于实现基于核心动画的粒子发射器系统。发射器层对象控制粒子的生成及其来源
CAGradientLayer 用于绘制填充图层形状的颜色渐变(在任何圆角的边界内)
CAMetalLayer 用于设置和提供可绘制纹理,以使用 Metal 渲染图层内容
CAEAGLLayer/CAOpenGLLayer 用于使用OpenGL ES (iOS) 或 OpenGL (OS X)设置渲染层内容的后备存储和上下文
CAReplicatorLayer 用于当你想自动拷贝一个或者多个子图层时。拷贝器会为你制作副本,并且使用你指定的属性值去修改副本的外观或属性
CAScrollLayer 用于管理一个由多个子图层组成的大的、可滑动的区域
CAShapeLayer 用于绘制立方形的贝斯曲线。 Shape layers 有利于绘制基于路径的形状,因为它们总是产生清晰的路径,而不是绘制到层的后备存储中的路径,后者在缩放时看起来不太好。然而,清晰的结果确实包括在主线程上渲染形状和缓存结果
CATextLayer 用于绘制文本或者富文本
CATiledLayer 用于管理大图像,该图像可分为较小的图块,并通过支持放大和缩小内容来单独呈现
CATransformLayer 用于渲染真正的3D图层层次,而不是由其他图层类实现的2D图层层次
QCCompositionLayer 用于渲染 Quartz Composer 结构(仅限 OS X)

提供图层的内容

图层是管理由你 app 提供的内容的数据对象。图层的内容由包含要显示的视觉数据的位图组成。你可以通过下面三种的任意一种方式来提供包含内容的位图:

  • 直接给图层对象的 contents 属性赋值一个 image 对象。(该方法适用于图层内容不改变或者很少改变的情况)
  • 给图层赋值一个委托对象(delegate object),让委托对象去绘制图层的内容。(该方法适用于图层内容周期性改变,并且可以由外部对象提供,例如视图。即 视图为图层的 delegate
  • 定义图层子类,通过重写它的绘制方法来自己提供图层内容。(如果你不得不创建自定义的图层类,或者你想改变图层的底层绘制行为,你应该用该方法)

你仅需要自己创建图层对象的时候去考虑为图层提供内容。如果你的 app 中都是图层支持的视图,你不需要担心使用上面提到的方法。图层支持视图会用高效的方法自动提供关联图层的内容。

使用 Image 作为图层的内容

因为图层只是管理位图图像(bitmap image)的一个容器,你可以直接给图层的 contents 属性赋值 image 对象。给图层赋值一个 image 是非常容易的,并且允许你指定想显示在屏幕上确切的 image 。图层使用你直接提供的 image 对象,不会试图创建 image 的副本。当你在多个地方使用同一个 image 时,这种行为会节省你的内存。

你赋值给图层的 image 必须是 CGImageRef 类型(在 OS X v10.6 及以后,你也可以赋值 NSImage 对象)。当赋值 image 时,记住需提供与本机分辨率匹配的图像。对于 Retina 显示屏,可能需要你去调整 image 对象 contentsScale 属性的值。关于对图层使用高分辨率内容的更多信息,请参见使用高分辨率图像

使用 Delegate 给图层提供内容

如果图层内容动态改变,你可以在需要的时候使用 delegate 对象来提供、更新图层内容。在显示时间,图层会调用 delegate 的方法去提供的需要的内容:

  • 如果 delegate 实现了 displayLayer: 方法,该方法会创建位图并将它赋值给图层的 contents 属性。
  • 如果 delegate 实现了 drawLayer:inContext: 方法,Core Animation 会创建位图,然后创建一个图形上下文(graphics context)来绘制该位图,最后调用 delegate 方法填充位图。delegate 方法必须在绘制到提供的图形上下文中。

delegate 对象必须实现 displayLayer:drawLayer:inContext: 方法。如果 delegate 实现了这两个方法,图层只会调用 displayLayer: 方法。

当 app 去加载或者创建想显示的位图时,重写 displayLayer: 方法是非常适合的。例 2-3 简单展示了如何实现 displayLayer: 方法。在该例中,delegate 使用了 helper 对象去加载显示它需要的 image。delegate 基于它内部状态来选择显示哪个 image,在该例中,内部状态是叫做 displayYesImage 的自定义属性。

例 2-3 直接设置图层内容

- (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];
    }
}

如果你没有预渲染的 image 或者 helper 对象去创建位图,delegate 可以使用 drawLayer:inContext: 方法动态绘制图层内容。例 2-4 展示了关于 drawLayer:inContext: 方法实现的简单例子。在该例中,delegate 使用固宽和当前渲染颜色绘制了一个简单的曲线路径。

例 2-4 绘制图层内容

- (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);
}

对有自定义内容的图层支持视图,应该继续重写视图的方法来做绘制。图层支持视图自动将自己设置为关联图层的 delegate,并且实现了需要实现的方法,你不应改变配置。你应该实现 drawRect: 方法来绘制你的内容。

在 OS X v10.8及以后的版本,绘图的另一个选择是:通过重写视图的 wantsUpdateLayerupdateLayer 方法提供位图。重写 wantsUpdateLayer 并返回 YES ,因为 NSView 类遵循备用渲染路径。代替调用 drawRect: ,视图调用 updateLayer 方法,该方法必须直接给图层 contents 属性赋值位图。在这种情况下,AppKit 希望直接设置视图图层对象的内容。

通过子类提供图层内容

如果你实现了自定义图层类,你可以重写你图层类的绘制方法来做任何绘制。对图层对象来说,生成本身自定义内容并不常见,但图层当然可以管理显示的内容。例如,CATiledLayer 类通过将大图像分割成若干小图像(因此可以独立管理和渲染它们)的方法来管理大图像。因为只有图层知道哪一个小图像在给定的时间上被渲染,所以图层直接管理绘制行为。

当创建自定义子类时,可以通过下面的两种方法来绘制图层内容:

  • 重写图层的 display 方法,使用该方法直接给图层的 contents 属性赋值。
  • 重写图层的 drawInContext: 方法,使用该方法绘制到提供的图形上线文中。

重写哪一个方法取决于你需要控制多少绘制过程。 display 方法是更新图层内容的主入口点,所以重写该方法可以完整的控制绘制过程。重写 display 方法也意味着你需要创建赋值给 contents 属性的 CGImageRef 类型的对象。如果你只是想绘制内容(或者让图层管理绘制操作)你可以重写 drawInContext: 方法,让图层创建备用存储(backing store)给你。

旋转你提供的内容

当你给图层的 contents 属性赋值图像时,图层的 contentsGravity 属性决定如何操作图像以配当前边界的。默认情况下,如果图像比当前边界更大或者更小,图层对象会将图像调整到有效空间内。如果图层边界的宽高比(aspect ratio)与图像的宽高比不同,会造成图像的变形。你可以使用 contentsGravity 属性确保内容得到了最好的展示。

可以赋给 contentsGravity 属性的值被分为两个类:

  • 基于位置的重力常数:允许你将图像固定到图层特定的边缘或角上,而无需缩放图像。
  • 基于缩放的重力常数:允许你使用选项去缩放图像,一些保持了宽高比,一些没有。

图 2-1 展示了基于位置的重力设置如何影响图像。除了 kCAGravityCenter 常量之外,每个常量都将图像固定在图层边界矩形的特定边缘或角上。kCAGravityCenter 常量将图像置于图层中心。这些常数都不会使图像缩放,所以图像总是以原尺寸进行渲染。如果图像比图层的边界大,可能会造成图像的部分内容被裁减;如果图像比图层的边界小,图层未被图像覆盖的部分会填充图层的背景色(如果设置的话)。

图2-1 图层基于位置的重力常数

图 2-2 显示了基于缩放的重力常量如何影响你的图像。如果图像尺寸没有完全和图层的尺寸一致的话,所有的常量都会缩放图像。它们直接的不同就是如何影响图像的原始宽高比。一些会保持宽高比,一些则不会。默认情况下,图层的 contentsGravity 属性设置为 kCAGravityResize 常量,它是唯一一个不保持图片宽高比的常量。

图 2-2 图层基于缩放的重力常数

使用高分辨率的图像

图层对于设备屏幕的分辨率一无所知。图层只存储了位图的指针,用给定的可用像素尽可能最好的方式显示它。如果你给图层的 contents 属性赋值了图像,你必须告诉 Core Animation 由图层 contentsScale 属性设定的合适的值来决定的图像分辨率。该属性默认值为 1.0 ,对于显示在标准分辨率屏幕上的图像来说,它是一个合适的值。如果你的图像放在 Retina 屏上,应该把值设置为 2.0。

仅在你直接给图层赋值位图的时候修改 contentsScale 属性的值。UIKit 和 AppKit 里图层支持的视图会基于屏幕分辨率和视图管理的内容自动给图层的设置一个合适的比例值。例如,如果你给 OS X 的图层 contents 属性值赋值 NSImage 对象,APPKit 会观察图像是否包含标准和高分辨两个类型的图像。如果有,APPKit 会使用正确的图像以适配当前屏幕,也会给 contentsScale 属性设置适配当前屏幕的值。

在 OS X 里,基于位置的重力常数会影响从指定给图层的图像对象中选择图像表示的方式。因为这些常数不会导致图像缩放,所以 Core Animation 依赖于 contentsScale 属性来选择具有最合适像素密度的图像表示。

在 OS X 里,图层的 delegate 可以实现 layer:shouldInheritContentsScale:fromWindow: 方法,并使用该方法去改变比例值。无论何时给定 window 的分辨率改变,AppKit 都会自动调用该方法, 这是可能的,因为图像会在标准分辨率和高分辨率的屏幕上移动。如果 delegate 支持改变图层图像的分辨率,那实现方法应该返回 YES 。当需要去反应新的分辨率时,该方法应该及时更新图层的内容。

调整图层的视觉样式和外观

图层对象有内嵌的视觉装饰,例如你可以在图层的主内容上添加边界线和背景色。因为这些视觉装饰不要求你做任何的渲染,所以它们使得在某些情况下可以将图层用作独立实体。你所需要做的就是设置图层的属性值,涂岑会处理必要的绘制,包括任何动画。有关这些视觉装饰如何影响图层外观的其他说明,请参见图层样式属性动画

图层有它们自己的背景和边界线

图层可以在基于图像的内容上显示填充满的背景色和描绘边框。背景色在图像内容后面绘制,边界线会在图像的上方绘制,如图 2-3 所示。如果图层包含子图层,它们也会出现在边界线的下方。因为背景色位于图像的后面,所以它会透过任何图像透明的部分。

图 2-3 给图层添加背景和边界线

例 2-5 展示了设置图层背景色和边界线所需的代码。这些都是可以进行动画的。

例 2-5  设置图层的背景色和边界线
// OC
myLayer.backgroundColor = [NSColor greenColor].CGColor;
myLayer.borderColor = [NSColor blackColor].CGColor;
myLayer.borderWidth = 3.0;

//Swift 
myLayer.backgroundColor = NSColor.green.cgColor
myLayer.borderColor = NSColor.black.cgColorlor
myLayer.borderWidth = 3.0

注意:对图层的背景色你可以使用任意类型的颜色,包括有透明度的颜色或者使用图案图像。当使用图案图像(pattern image)时, 要注意到Core Graphics 会使用标准坐标系处理图案图像的渲染,这和 iOS 上默认的坐标系是不同的。因此,除非你反转坐标,否则在 iOS 上渲染的图像默认会颠倒显示。

如果你给图层背景色设置了不透明颜色,考虑将图层的 opaque 属性设置为 YES 。这样做可以提高屏幕合成图层时的性能,并消除图层后台存储管理 alpha 通道的需要。但是,如果图层的角半径也非零,则不能将其标记为不透明。

图层支持设置角半径

你可以通过给图层添加一个角半径来实现矩形圆角的效果。角半径是视觉修饰,通过遮罩图层边界角的一部分来允许底层内容的显示,如图 2-4 显示。因为它涉及透明遮罩,所以角半径不会影响图层 contents 属性的 image,除非 masksToBounds 属性值为 YES 。但,角半径总是会影响图层的背景色和边线的绘制。

图 2-4 图层上的圆角
设置图层 cornerRadius 属性的值可以为图层设置角半径。你指定的半径值使用点为单位,并且在图层显示前会应用到图层的四个角。

图层支持内嵌阴影

CALayer 包含几个属性可以用来配置阴影效果。阴影通过使图层看起来像漂浮在底层内容之上来增加图层的深度。这是另一个视觉装饰,在某些情况下这是非常有用的。通过图层,你可以控制阴影的颜色、相对于图层内容的位置、透明度和形状。

图层阴影的透明度的值默认为 0,这会有效的隐藏阴影。将透明度改成一个非零值,会使 Core Animation 绘制阴影。因为阴影默认的位置是在图层下面的,在你看到它之前,需要你改变阴影的偏移量。记住下面这一点是很重要的,你指定阴影的偏移量是使用图层原始坐标系来应用的,而 iOS 和 OS X 是不同的。图 2-5 显示了在右下方有阴影的图层。在 iOS 上,y 轴的值应该是正数,而在 OS X 上面 y 轴的值应该为负数。

图 2-5 在图层上应用阴影

当在图层上添加阴影时,阴影是图层内容的一部分,但实际上它会延展到图层边界之外。因此,如果你将图层的 masksToBounds 属性设置为 YES ,边界之外阴影的效果也会被剪切掉。如果你的图层包含任何透明内容,会造成一个奇怪的效果:在图层下面阴影的部分仍然可见,但是在图层边界之外的是不可见的。如果你想同时使用阴影和边界蒙版,你应该用两个图层来代替。对包含你内容的图层应用蒙版,再将该图层嵌入到有阴影效果的完全相同尺寸的图层中。

关于如何对图层应用阴影的例子,请参见阴影属性

给 OS X 视图添加过滤器视觉效果

在 OS X app 里,你可以直接对图层内容应用 Core Image 过滤器。你可以通过过滤器来使图层内容模糊或者锐化,去改变颜色、扭曲内容、执行很多其他类型的操作。例如,图像处理程序可能会使用过滤器无损的修改图像,视频编辑程序可能使用它们实现不同类型的视频过渡效果。并且因为过滤器是用硬件应用在图层内容上的,所以渲染是很快且平滑的。

注意:你不能给 iOS 上的图层添加过滤器。

对于给定的图层,你可以对图层内容的前景和背景应用过滤器。前景内容由图层自己包含的所有内容组成,包含 contents 属性的图像、背景色、边界线和子图层的内容。背景内容是直接位于图层下方但实际上不是图层本身的一部分的内容。大多数图层的背景内容是它的直接父图层,其父图层可能整个或者部分被其遮罩。例如,当你想用户专注于图层前景内容时,会对图层背景应用模糊过滤器。

你可以通过下面的图层属性来添加 CIFilter 类型的指定过滤器:

  • filters 属性包含一个数组,数组元素为仅能影响图层前景的过滤器。
  • backgroundFilters 属性包含一个数组,数组元素为仅能影响图层背景的过滤器。
  • compositingFilter 属性定义了图层的前景和背景内容如何被组合在一起。

为了给图层添加过滤器,你必须定位、创建 CIFilter 对象,并在对象添加到图层之前将它配置好。 CIFilter 类包含几个用来定位可用 Core Image 过滤器的类方法,如 filterWithName: 。创建过滤器只是第一步,很多过滤器有定义过滤器如何修改图像的参数。例如,盒子模糊过滤器有一个输入半径的参数,该参数用于应用的过滤器的数量。作为过滤器配置过程的一部分,你应该始终提供这些参数的值。但是,一个不需要指定的常见参数是输入图像,它由图层本身提供。

当向图层添加过滤器时,最好在过滤器添加到图层之前配置过滤器的参数。主要原因是:一旦将过滤器添加到图层上,你就不能 CIFilter 对象本身来修改它了。但是,在添加到图层之后,你可以使用图层的 setValue:forKeyPath:方法来修改。

例 2-6 展示了如何创建收缩失真滤波器并将其应用到图层上。此滤镜向内收缩图层的源像素,最大程度地扭曲最靠近指定中心点的像素。注意在该例中你不需要指定过滤器的输入图像,因为图层的图像会被自动使用。

例 2-6 对图层应用过滤器

CIFilter* aFilter = [CIFilter filterWithName:@"CIPinchDistortion"];
[aFilter setValue:[NSNumber numberWithFloat:500.0] forKey:@"inputRadius"];
[aFilter setValue:[NSNumber numberWithFloat:1.25] forKey:@"inputScale"];
[aFilter setValue:[CIVector vectorWithX:250.0 Y:150.0] forKey:@"inputCenter"];
 
myLayer.filters = [NSArray arrayWithObject:aFilter];

有关可用 Core Image 过滤器的信息,请参见Core Image Filter 参考

OS X 的视图图层重绘策略会影响性能

在 OS X 里,图层支持视图支持几个不同的策略,用来决定何时更新底部图层的内容。因为原生 AppKit 绘制模型和由 Core Animation 介绍的方法是不同的,这些策略使你将老代码迁移到 Core Animation 技术上变得更加简单。你可以逐个视图地配置这些策略,以确保每个视图的最佳性能。

每个视图都定义了 layerContentsRedrawPolicy 方法,用来返回是图图层的重绘策略。你可以使用 setLayerContentsRedrawPolicy 方法来设置策略。为了保持与传统绘图模型的兼容性,AppKit 默认情况下会将重绘策略设置为 NSViewLayerContentsRedrawDuringViewResize。但是,你可以将策略改为表 2-2 的任意一个值。注意推荐的重绘策略并不是默认的那个。

表 2-2 OS X 视图的图层重绘策略

策略 用法
NSViewLayerContentsRedrawOnSetNeedsDisplay 这是推荐的策略。用该策略,视图几何图形的改变不会自动造成视图去更新图层的内容。取而代之的是,拉伸和操纵图层的现有内容,以方便几何图形的更改。想强制视图重绘它自身,并更新图层的内容,你必须显示的调用图层的 setNeedsDisplay 方法。 该策略最接近于 Core Animation 图层的标准行为。但是,它不是默认策略,必须显式设置。
NSViewLayerContentsRedrawDuringViewResize 这是默认的重绘策略。该策略通过无论何时视图几何图形改变,都会重新缓存图层的内容的方法,与传统 AppKit 绘制保持了最大的兼容性。该行为会造成视图的 drawRect: 方法在重新设置尺寸期间在 app 主线程多次调用。
NSViewLayerContentsRedrawBeforeViewResize 使用该策略,在任何调整尺寸的操作之前,AppKit 以最终尺寸绘制图层,并缓存该位图。调整尺寸操作使用缓存位图作为起始图像,缩放它以适应旧的边界矩形。然后,它将位图动画化为最终尺寸。这种行为会导致视图内容在动画开始时出现拉伸或扭曲,在初始外观不重要或不明显的情况下效果会更好。
NSViewLayerContentsRedrawNever 使用策略,即使调用 setNeedsDisplay: 方法,AppKit 也不会更新图层。此策略最适合内容从不改变的视图,以及视图大小很少改变(如果有的话)的视图。例如,你可以将它用于显示固定大小内容或背景元素的视图。

视图重绘策略减轻了使用独立子图层来提高绘图性能的需要。在引入视图重绘策略之前,有些图层支持视图比需要的绘制频率更高,从而导致性能问题。解决这些性能问题的方法是使用子图层来呈现视图内容中不需要定期重绘的部分。随着重绘策略在 OS X v10.6 中的引入,现在建议将图层层支持视图的重绘策略设置为适当的值,而不是创建显式子图层结构。

给图层添加自定义属性

CAAnimation 和 CALayer 类延伸了键值编码约定来支持自定义属性。你可以使用该行为来给图层添加数据,然后使用你自定义的键来获取。你甚至可以给你的自定义属性关联事件,以至于当你改变属性时,可以执行响应的动画。

有关如何设置和获取自定义属性的信息,请参见符合键值编码的容器类。有关向图层对象添加动作的信息,请参见改变图层的默认行为

打印图层支持视图的内容

在打印过程中,图层会根据需要重新绘制其内容,以适应打印环境。Core Animation 通常在渲染到屏幕上时依赖于缓存的位图,但在打印时会重绘该内容。特别是,如果一个图层支持视图使用 drawRect: 方法来提供图层内容,Core Animation 在打印过程中再次调用 drawRect: 来生成打印的图层内容。

最后