设置图层对象
图层对象是您使用Core Animation所做的一切的核心。图层管理应用程序的视觉内容,并提供用于修改该内容的样式和视觉外观的选项。尽管iOS应用已自动启用了层支持,但是OS X应用的开发人员必须先显式启用它,然后才能利用性能优势。启用后,您需要了解如何配置和操作应用程序的图层以获得所需的效果。
在您的应用中启用核心动画支持
在iOS应用中,始终启用Core Animation,并且每个视图都有一个图层支持。
更改与视图关联的图层对象
层支持的视图CALayer默认情况下会创建该类的实例,并且在大多数情况下,您可能不需要其他类型的层对象。但是,Core Animation提供了不同的图层类,每个图层类都提供了您可能会发现有用的专门功能。选择不同的图层类可能使您能够以简单的方式提高性能或支持特定类型的内容。例如,CATiledLayer优化该类以有效显示大型图像。
更改UIView使用的图层类
您可以通过覆盖iOS视图的layerClass方法并返回其他类对象来更改iOS视图使用的图层类型。大多数iOS视图都会创建一个CALayer对象,并将该层用作其内容的后备存储。对于您自己的大多数视图,此默认选择是一个不错的选择,您无需更改它。但是您可能会发现在某些情况下,不同的图层类更合适。例如,在以下情况下,您可能想要更改图层类:
您的视图使用Metal或OpenGL ES绘制内容,在这种情况下,您将使用CAMetalLayer或CAEAGLLayer对象。
有一个专门的图层类可提供更好的性能。
您想利用一些特殊的Core Animation图层类,例如粒子发射器或复制器。
更改视图的图层类非常简单;清单2-1中显示了一个示例。您所要做的就是重写layerClass方法并返回要使用的类对象。在显示之前,视图将调用该layerClass方法并使用返回的类为其自身创建一个新的图层对象。创建后,视图的图层对象无法更改。
清单2-1 指定iOS视图的图层类
+ (Class) layerClass { return [CAMetalLayer class]; }
不同的层类别提供特殊的行为
核心动画定义了许多标准层类,每个标准层类都是针对特定用例设计的。
CALayer类是所有层对象的根类。它定义了所有图层对象必须支持的行为,并且是图层支持的视图使用的默认类型。但是,您也可以在表2-1中指定一个图层类。
提供图层的内容
图层是用于管理应用程序提供的内容的数据对象。图层的内容由包含要显示的视觉数据的位图组成。您可以通过以下三种方式之一提供该位图的内容:
将图像对象直接分配给图层对象的contents属性。(此技术最适合从未更改或很少更改的图层内容。)
将委托对象分配给图层,并让委托绘制图层的内容。(此技术最适合可能会定期更改并且可以由外部对象(例如视图)提供的图层内容。)
定义图层子类并覆盖其绘制方法之一以自己提供图层内容。(如果您仍然必须创建一个自定义图层子类,或者想要更改该图层的基本图形行为,则此技术是适当的。)
唯一需要担心为图层提供内容的时间是您自己创建图层对象时。如果您的应用仅包含基于图层的视图,则无需担心使用任何前述技术来提供图层内容。支持层的视图会自动以最有效的方式为其关联层提供内容。
使用图像作为图层的内容
由于图层只是用于管理位图图像的容器,因此可以将图像直接分配给图层的contents属性。将图像分配到图层很容易,并且可以指定要在屏幕上显示的确切图像。图层使用您直接提供的图像对象,并且不尝试创建该图像的自己的副本。如果您的应用在多个地方使用相同的图像,此行为可以节省内存。
您分配给图层的图像必须是一种CGImageRef类型。(在OS X v10.6和更高版本中,您还可以分配一个NSImage对象。)分配图像时,请记住提供分辨率与本机设备的分辨率匹配的图像。对于配备Retina显示屏的设备,这可能还需要您调整contentsScale图像的属性。有关在图层上使用高分辨率内容的信息,请参见使用高分辨率图像。
使用委托提供图层内容
如果图层的内容动态变化,则可以使用委托对象在需要时提供和更新该内容。在显示时,该层调用委托的方法以提供所需的内容:
如果您的委托实现了该displayLayer:方法,则该实现负责创建位图并将其分配给图层的contents属性。
如果您的委托实现了该drawLayer:inContext:方法,则Core Animation将创建一个位图,创建一个图形上下文以绘制到该位图中,然后调用您的委托方法以填充该位图。您的委托方法要做的就是绘制到提供的图形上下文中。
委托对象必须实现displayLayer:or drawLayer:inContext:方法。如果委托同时实现displayLayer:和drawLayer:inContext:方法,则该层仅调用displayLayer:方法。
覆盖此displayLayer:方法最适合于您的应用希望加载或创建其要显示的位图的情况。清单2-3显示了displayLayer:委托方法的示例实现。在此示例中,委托使用助手对象加载并显示所需的图像。委托方法根据其内部状态选择要显示的图像,在示例中,该内部状态称为的自定义属性displayYesImage。
清单2-3 直接设置图层内容
-(void)displayLayer:(CALayer *)theLayer {
//检查某些状态属性的值
如果(self.displayYesImage){
//显示是图片
theLayer.contents = [someHelperObject loadStateYesImage];
}
其他{
//显示无图片
theLayer.contents = [someHelperObject loadStateNoImage];
}
}
如果您没有预渲染的图像或帮助对象来为您创建位图,则委托可以使用该drawLayer:inContext:方法动态绘制内容。清单2-4显示了该drawLayer:inContext:方法的示例实现。在此示例中,委托人使用固定宽度和当前渲染颜色绘制一条简单的弯曲路径。
清单2-4 绘制图层内容
-(void)drawLayer:(CALayer *)theLayer inContext:(CGContextRef)theContext {
CGMutablePathRef thePath = CGPathCreateMutable();
CGPathMoveToPoint(thePath,NULL,15.0f,15.f);
CGPathAddCurveToPoint(thePath,
空值,
15.f,250.0f,
295.0f,250.0f,
295.0f,15.0f);
CGContextBeginPath(theContext);
CGContextAddPath(theContext,thePath);
CGContextSetLineWidth(theContext,5);
CGContextStrokePath(theContext);
//释放路径
CFRelease(thePath);
}
对于具有自定义内容的支持图层的视图,您应继续覆盖该视图的方法来进行绘制。层支持的视图自动使自己成为其层的委托并实现所需的委托方法,并且您不应更改该配置。相反,您应该实现视图的drawRect:方法来绘制内容。
在OS X v10.8和更高版本中,图形的替代方法是通过覆盖视图的wantsUpdateLayer和updateLayer方法来提供位图。重写wantsUpdateLayer和返回YES会导致NSView类遵循替代的渲染路径。drawRect:视图不是调用,而是调用您的updateLayer方法,该方法的实现必须直接将位图分配给图层的contents属性。这是AppKit希望您直接设置视图的图层对象的内容的一种情况。
通过子类提供层内容
如果仍然要实现自定义图层类,则可以覆盖图层类的绘制方法以进行任何绘制。图层对象本身生成自定义内容并不常见,但是图层当然可以管理内容的显示。例如,CATiledLayer该类通过将大图像分成较小的图块来管理大图像,这些小图块可以单独管理和呈现。因为只有图层具有在任何给定时间需要渲染哪些图块的信息,所以它直接管理绘图行为。
子类化时,可以使用以下两种技术之一来绘制图层的内容:
覆盖图层的display方法,并使用它直接设置contents图层的属性。
覆盖图层的drawInContext:方法,并使用它绘制到提供的图形上下文中。
覆盖哪种方法取决于在绘制过程中需要多少控制。该display方法是更新图层内容的主要入口点,因此,覆盖该方法可以使您完全控制流程。覆盖该display方法还意味着您有责任创建CGImageRef要分配给该contents属性的。如果只想绘制内容(或让图层管理绘制操作),则可以改写drawInContext:方法,然后让图层为您创建后备存储。
调整您提供的内容
当您将图像分配给contents图层的contentsGravity属性时,图层的属性确定如何操作该图像以适合当前边界。默认情况下,如果图像大于或小于当前范围,则图层对象将缩放图像以适合可用空间。如果图层边界的宽高比与图像的高宽比不同,则可能导致图像失真。您可以使用该contentsGravity属性来确保以最佳方式呈现您的内容。
可以分配给contentsGravity属性的值分为两类:
基于位置的重力常数使您可以将图像固定到图层边界矩形的特定边缘或角落,而无需缩放图像。
基于缩放的重力常数使您可以使用多个选项之一拉伸图像,其中一些选项保留长宽比,而某些选项则不保留。
图2-1显示了基于位置的重力设置如何影响图像。除kCAGravityCenter常数外,每个常数都将图像固定到图层边界矩形的特定边缘或角落。该kCAGravityCenter常数使图像在图层中居中。这些常量都不会使图像以任何方式缩放,因此图像始终以其原始大小进行渲染。如果图像大于图层边界,则可能会导致图像的某些部分被裁剪;如果图像较小,则该图层中未被图像覆盖的部分会显示该图层的背景色(如果设置)。
图2-1 图层的基于位置的重力常数
图2-2显示了基于缩放的重力常数如何影响图像。如果所有这些常量都不完全适合图层的边界矩形,则将缩放图像。模式之间的差异在于它们如何处理图像的原始宽高比。有些模式保留它,而另一些则不保留。默认情况下,图层的contentsGravity属性设置为kCAGravityResize常量,这是唯一不保留图像纵横比的模式。
图2-2 图层的基于缩放的重力常数
使用高分辨率图像
层不具备底层设备屏幕分辨率的任何固有知识。图层只是存储指向位图的指针,并在给定可用像素的情况下以最佳方式显示它。如果将图像分配给图层的contents属性,则必须通过将图层的contentsScale属性设置为适当的值来告知Core Animation该图像的分辨率。该属性的默认值为,1.0适用于打算在标准分辨率屏幕上显示的图像。如果您的图像打算用于Retina显示屏,请将此属性的值设置为2.0。
contentsScale仅当您直接为图层分配位图时,才需要更改属性的值。UIKit和AppKit中基于图层的视图会根据屏幕分辨率和该视图管理的内容,自动将其图层的比例因子设置为适当的值。
调整图层的视觉样式和外观
图层对象具有内置的视觉装饰,例如边框和背景色,可用于补充图层的主要内容。由于这些视觉装饰不需要您进行任何渲染,因此在某些情况下,它们可以将图层用作独立实体。您所要做的就是在图层上设置一个属性,图层将处理必要的图形,包括所有动画。
图层具有自己的背景和边界
除基于图像的内容外,图层还可以显示填充的背景和描边边框。如图2-3所示,背景色呈现在图层内容图像的后面,边框呈现在该图像的顶部。如果图层包含子图层,它们也将出现在边框下方。因为背景色位于图像的后面,所以该颜色会在图像的任何透明部分发光。
图2-3 为图层添加边框和背景
清单2-5显示了设置图层的背景颜色和边框所需的代码。所有这些属性都是可动画的。
清单2-5 设置图层的背景颜色和边框
myLayer.backgroundColor = [NSColor greenColor..CGColor;
myLayer.borderColor = [NSColor blackColor] .CGColor;
myLayer.borderWidth = 3.0;
注意: 可以将任何类型的颜色用作图层的背景,包括具有透明性或使用图案图像的颜色。但是,在使用图案图像时,请注意,Core Graphics会处理图案图像的渲染,并使用其标准坐标系进行处理,这与iOS中的默认坐标系不同。这样,默认情况下,除非翻转坐标,否则在iOS上渲染的图像默认为上下颠倒。
如果将图层的背景色设置为不透明颜色,请考虑将图层的opaque属性设置为YES。这样做可以提高在屏幕上合成图层时的性能,并且不需要图层的后备存储来管理Alpha通道。但是,如果图层的拐角半径也非零,则不能将其标记为不透明。
图层支持角半径
您可以通过在图层上添加圆角半径来为图层创建圆角矩形效果。拐角半径是一种视觉装饰,可遮盖图层边界矩形的部分拐角,以使基础内容得以显示,如图2-4所示。因为它涉及应用透明蒙版,所以contents除非将masksToBounds属性设置为YES,否则角半径不会影响图层属性中的图像。但是,拐角半径始终会影响图层背景颜色和边框的绘制方式。
图2-4 层上的拐角半径
要将角半径应用到图层,请为图层的cornerRadius属性指定一个值。您指定的半径值以磅为单位测量,并在显示之前应用于图层的所有四个角。
图层支持内置阴影
所述CALayer类包括用于配置阴影效果几个属性。阴影使图层看起来像是漂浮在其基础内容之上,从而增加了图层的深度。这是另一种视觉装饰,您可能会发现它在特定情况下对您的应用很有用。使用图层,您可以控制阴影的颜色,相对于图层内容的位置,不透明度和形状。
0默认情况下,图层阴影的不透明度值设置为,可有效隐藏阴影。将不透明度更改为非零值会导致Core Animation绘制阴影。由于阴影默认情况下位于图层的正下方,因此您可能还需要更改阴影的偏移量才能看到它。不过,请务必记住,为阴影指定的偏移量是使用图层的本机坐标系应用的,iOS和OS X上的本机坐标系是不同的。图2-5显示了一个阴影向下延伸到图层的图层。层的右边。在iOS中,这需要为y轴指定一个正值,但在OS X中,该值必须为负。
图2-5 将阴影应用于图层
将阴影添加到图层时,阴影是图层内容的一部分,但实际上延伸到图层的边界矩形之外。因此,如果启用masksToBounds图层的属性,阴影效果将被修剪到边缘。如果您的图层包含任何透明内容,这可能会导致奇怪的效果,即阴影的直接在图层下面的部分仍然可见,但超出图层的部分则看不到。如果要阴影但也要使用边界遮罩,请使用两层而不是一层。将蒙版应用于包含您的内容的图层,然后将该图层嵌入与启用阴影效果的大小完全相同的第二图层中。
有关如何将阴影应用于图层的示例,请参见“ 阴影属性”。
将自定义属性添加到图层
在CAAnimation和CALayer类扩展了键值编码规范,支持自定义属性。您可以使用此行为将数据添加到图层,并使用定义的自定义键检索数据。您甚至可以将动作与自定义属性相关联,以便在更改属性时执行相应的动画。
有关如何设置和获取自定义属性的信息,请参见符合键值编码的容器类。有关将动作添加到图层对象的信息,请参见“ 更改图层的默认行为”。
打印层支持的视图的内容
在打印期间,图层会根据需要重新绘制其内容以适应打印环境。尽管Core Animation在渲染到屏幕时通常依赖于缓存的位图,但在打印时会重新绘制该内容。特别是,如果支持图层的视图使用该drawRect:方法提供图层内容,则Core Animation drawRect:在打印期间会再次调用以生成打印的图层内容。
网友评论