Core Animation--2.构建图层对象

构建图层对象
 
图层是Core Animation的核心。图层管理着应用程序的可视内容,图层提供了更改内容样式与可视外观的选项。iOS是自动支持图层的,而如果你是一名OS X开发者,那你必须手动开启图层支持。一旦开启了图层支持,你必须理解如何配置和操控图层以获得你想要的效果。
 
开启对Core Animation的支持
在iOS app中,Core Animation总是开启的,视图也都是支持图层的。而在OS X中,app必须显示地通过以下方式开启对Core Animation的支持:
 
Ø链接QuartzCore框架(如果iOS app显式地使用了Core Animation则必须链接此框架)。
Ø你可选择以下任意一种方式为NSView开启图层支持:
  u在你的nib文件中,使用View Effect检视面板让视图支持图层。检视面板会对选中的视图和它的子视图显示一个复选框。推荐做法是尽可能的在窗口的内容视图中开启图层支持。
  u对于使用代码创建的视图,调用视图的setWantsLayer:方法,并传入一个YES值以表明当前的视图是支持图层的。
 
使用上面所提到的方式开启图层支持可创建一个支持图层的视图。系统负责创建视图底层的图层对象,并保持对图层的更新。在OS X中,也可以创建图层托管视图,该视图底层的图层由你的app创建和管理(你不能在iOS中创建图层托管视图)。
 
改变与视图相关联的图层对象
支持图层的视图默认会创建一个CALayer实例,一般情况下你可能不需要不同类型的图层对象。但是Core Animation提供了一些不同类型的图层类,每个图层类型都拥有特殊的功能。选择某个图层类可能会提升app的性能或者能以简单的方式支持指定的内容类型。比如CATiledLayer类在显示大图片上将更加高效。
 
改变视图自有的图层类 
你可以通过覆盖iOS视图中的layerClass方法并返回一个需要的图层类对象。大部分的iOS视图通过创建一个CALayer对象,使用该对象储备视图的内容。使用默认的图层类型是个不错的选择,但在某些情况下,你可能需要使用特定特性的图层对象。比如在下述情况下你需要改变图层的类型:
 
Ø视图的绘图内容是由OpenGL ES实现,此种情况你需要使用CAEAGLLayer对象。
Ø特殊的图层让你拥有更强的表现性能。
Ø需要利用某些特殊的Core Animation类,比如粒子发射器或者拷贝器。
 
改变一个视图的图层类型非常的简单;所有你需要做得只是覆盖layerClass方法并返回一个你想要替代的类对象。如清单2-1所示,视图调用layerClass方法并使用返回的类为其创建新的图层对象。一旦创建完成,视图的图层对象将不可改变。
 
清单2-1 一个iOS视图图层类的指定
 
 
 
  1. +(Class) layerClass { 
  2.     return [CAEAGLLayer class]; 
 
 
不同的图层拥有特定的行为
Core Animation定义了许多标准的图层类,每一个图层类都有着各自的应用场景。CALayer类是所有图层对象的根类,它定义了所有图层对象必须支持的行为,它也是图层支持的视图的默认图层类型。你可以指定表2-1中的某一个图层类以改变默认的图层类型。
 
提供图层的内容
图层是管理app内容的数据对象。图层的内容由包含可视数据的位图构成。使用下述三种方式之一可给提供图层的内容:
l直接赋值一个UIImage对象给图层对象contents属性。(这个技术适用于图层内容从不或几乎不改变的情形。)
l赋值一个代理给图层,由代理负责绘制图层内容。(该技术适用于图层内容可能偶尔改变,且内容可由外部对象提供,比如视图。)
l定义一个CALayer的子类并覆盖类的绘图方法,有覆盖的方法返回图层的内容。(该技术适用于你需要创建自定义图层的子类,或者你想改变图层基本的绘图行为。)
 
你需要为提供图层内容而担心的时刻仅在手动创建图层对象。如果你的app中只包含支持图层的视图。那你不需要担心使用刚刚提到的这些提供图层的方法提供图层内容。支持图层的视图会使用尽可能高效的方式为与之相关的图层提供内容。
 
使用图片为图层提供内容
因为一个图层仅是管理位图图片的容器,所以你可以直接赋值一个图片给图层的contents属性。赋值一个图片给图层很简单,只需要指定一张你想显示在屏幕上的具体的图片就可以了。图层将直接使用你提供的图片对象,并不会尝试创建自己的图片拷贝。当你的应用使用相同的图片在多个地方时,该行为可以节省许多内存。
 
你赋值的图片类型必须是CGImageRef类型(在OS X 10.6或之前版本,你也可以赋值一个NSImage对象。)当赋值图片时,记住提供的图片的分辨率要与本地设备的分辨率相匹配。对于Retina显示设备,这可能也需要你去调整图片的contentsScale属性。
 
使用代理提供图层的内容
如果图层的内容是动态改变的,你可以使用一个代理对象在需要的时候提供图层并更新内容。图层显示的时候,图层调用你的代理方法以提供需要的内容:
 
Ø如果你的代理实现了displayLayer:方法,实现方法负责创建位图并赋值给contents属性。
Ø 如果你的代理实现的是drawLayer:inContext:方法,Core Animation创建一个位图,创建一个用于绘制位图的上下文,并调用代理方法填充该位图。你的代理方法所要做的是将内容画在图形上下文上。
代理对象必须实现displayLayer:或者drawLayer:inContext方法之一。如果代理对象把这两个方法都实现了,图层只调用displayLayer:方法。
 
覆盖displayLayer:方法在当你的app更倾向于载入或创建想要显示的位图的情况下适用。清单2-3显示了一个实现displayLayer:代理方法的示例代码。在这个例子中,代理使用了一个辅助对象来加载和显示它需要的图片。代理方法根据内部的状态选择哪张图片用于显示。例子中得displayYesImage是一个自定义属性。
 
清单2-3:直接设置图层的内容
 
 
  1.     //检查某些属性状态值 
  2.     if (self.displayYesImage) { 
  3.     // 显示Yes图片 
  4.     theLayer.contents = [someHelperObject loadStateYesImage]; 
  5.    } else { 
  6.      // 显示No图片 
  7.      theLayer.contents = [someHelperObject loadStateNoImage]; 
  8.    } 
  9. }199/blog/152467#OSC_h1_1 
 
如果你没有预渲染的图片或者辅助对象来创建位图。代理对象可以使用drawLayer:inContext:方法动态的绘制内容。清单2-4显示了一个对drawLayer:inContext方法实现的例子。在该例子中,代理对象使用了固定的宽度和当前的渲染颜色绘制了一个简单的曲线路径。
 
清单2-4:绘制图层上下文
 
 
 
  1. - (void)drawLayer:(CALayer *)theLayer inContext:(CGContextRef)theContext { 
  2.   CGMutablePathRef thePath = CGPathCreateMutable(); 
  3.   CGPathMoveToPoint(thePath,NULL,15.0f,15.f); 
  4.   CGPathAddCurveToPoint(thePath, 
  5.                               NULL, 
  6.                               15.f,250.0f, 
  7.                               295.0f,250.0f, 
  8.                               295.0f,15.0f); 
  9.    CGContextBeginPath(theContext); 
  10.    CGContextAddPath(theContext, thePath); 
  11.    CGContextSetLineWidth(theContext, 5); 
  12.    CGContextStrokePath(theContext); 
  13.    // Release the path 
  14.    CFRelease(thePath); 
 
带有自定义内容并支持图层的视图,你应该去覆盖视图的绘图方法。一个支持图层的视图自动创建它自己的图层代理并实现需要的代理方法,你不应该改变这个配置。相反,你应该实现你视图的drawRect:方法以绘制你的内容。
 
在OS X 10.8和之前的版本中,绘图的另外方法是通过覆盖你视图的wantsUpdateLayer和updateLayer方法提供一个位图。覆盖wantsUpdateLayer并返回YES会引起NSView类调用替换的渲染路径。相对于调用drawRect:方法,视图调用你的updateLayer:方法,方法的实现必须直接赋值一个位图给图层的contents属性。这个AppKit期望你直接设置视图的图层对象内容的一种方案。
 
由子类提供图层的内容
如果你实现了一个自定义的图层类,你可以覆盖图层类的绘图方法完成任何绘图的操作。用这种方法生成图层对象的自定义内容是罕见的,但是某些图层却拥有管理显示的内容能力。如CATiledLayer类通过将大图片拆成更小的可管理、可独立渲染的碎片来管理大的图片。因为只有图层知道在某一时刻哪一个碎片需要被渲染,图层会直接管理绘图的行为。
 
当子类化图层类,你可使用下述的两种方式绘制你的图层内容:
 
l   覆盖图层的display方法并使用在方法中直接设置图层的contents属性。
l   覆盖图层的drawInContext:方法并将需要的内容绘制到提供的图形上下文中。
 
选择何种方法依赖于在绘图过程中你需要多少的控制。display方法是更新图层内容的主要入口点,所以覆盖这个方法让你处于完全的过程控制中。覆盖display方法也意味你需要负责contents属性创建CGImageRef对象。如果你只是想绘制内容(或让你的图层管理绘图操作),你可以覆盖drawInContext:方法并让图层为你创建内容储备。
 
调整提供的内容
当给图层的contents属性赋值一个图片,图层的contentsGravity属性确定图片如何适合当前的边界。默认情况下,如果一个图片大于小于当前的边界,图层对象缩放图片以适应有效的空间。如果图层的长宽比和图片的长宽比不一致,这会导致图片被扭曲。所以你可以使用contentsGravity属性来确定你的内容以最佳的方式被呈现。
 
你可以向contentsGravity属性赋予的值分为两个分类:
 
l基于位置的引力约束允许你固定你的图片到图层矩形边界的一个特殊的边缘或角落,不会缩放图片。
l基于缩放的引力约束允许你伸缩图片使用多个选项之一,某些选项保留长宽比,有些则不保留。
 
图2-1 显示了基于位置的引力设置如何影响你的图片。除了kCAGravityCenter约束,每一个约束都将图片固定在图层矩形边界的某个边缘或角落。kCAGravityCenter约束将图片居中在图层中。基于位置的引力约束没有一个选项会缩放图片。所以图片总是和它原始的尺寸一样的情况下被渲染。如果你的图片大于图层的边界,这可能导致部分图片被裁减;如果图片小于矩形边界,如果设置了背景颜色的话,则图片没有覆盖到得区域则显示图层的背景颜色。
图2-1 基于位置的图层引力约束
 
图2-2显示了基于缩放的引力约束如何影响你的图片。如果图片不是准确匹配图层矩形边界范围内,所有这些约束都会缩放你的图片。这些模式选项之间所不同的是如何处理图片原始的长宽比。一些模式保留原始图片的长宽比,有些则不保留。默认情况是,图层的contentsGravity属性被设置为kCAGravityResize常量值,它是唯一一个不保留图片的长宽比的选项。
图2-2 基于缩放的图层引力约束
 
使用高分辨率的图片
图层并不知道当前设备的分辨率信息。图层只是简单的存储一个指向位图的指针,并用给定的有效像素以最佳的方式显示。如果你赋值一个图片给图层的contents属性,你必须给图层的contentsScale属性设置一个正确的值以告诉Core Animation关于图片的分辨率。默认的属性值为1.0,对于在标准分辨率的屏幕上显示图片是正确的。如果你的图片要在Retina屏幕上显示,该值需要设定为2.0。使用[[UIScreen mainScreen] scale]可获取正确的缩放率。
 
仅在直接赋值一副位图给图层才需要改变contentScale属性的值。在UIKit或AppKit中,一个支持图层的视图会被自动设置基于屏幕分辨率的图层缩放因子,并且图层内容由视图管理。
 
在OS X中,基于位置的引力约束影响图片呈现的方式。从NSImage对象选择赋值给图层。因为这些实例不会引起图片的缩放。Core Animation依靠contentsScale属性以最佳的像素密度呈现图片。
 
在OS X中,图层的代理可以实现layer:shouldInheritContentScale:fromWindow:方法,使用该方法响应对缩放因子的改变。可能因为窗口会在标准分辨率和高分辨率屏幕之间移动,AppKit会在窗口分辨率变化时自动调用代理的实现方法。如果代理支持图层的图片分辨率的变化,则此方法返回YES。当需要反映分辨率的变化,该方法会更新图层的内容。
 
调整图层的可视样式和外观
图层对象拥有内建的可视装饰,如边框、背景色。你可以使用这些装饰对图层的主内容进行补充。因为这些可视的装饰不需要任何渲染。装饰让图层在一些情况下让图层成为独立的实体成为可能。你只需设置图层的属性,图层自会处理必要的绘图工作,包括动画。
 
图层拥有自己的背景和边框
图层除了可以显示基于图片的内容,还可以显示被填充的背景、描边的边框。背景被渲染在图层的内容图片的后方,边框被渲染在内容图片的前方。如图2-3显示。如果图层包含子层,子层也会显示在边框的下方。因为背景颜色是处在图片的后方的,背景会从图片的任何透明的地方透射出来。
 
图2-3 给图层添加边框和背景色
 
清单2-5显示了设置图层的边框和背景颜色代码。所有的这些属性都是可动画的。
 
清单2-5:设置图层的背景色和边框
 
 
  1. myLayer.backgroundColor = [NSColor greenColor].CGColor; 
  2. myLayer.borderColor = [NSColor blackColor].CGColor; 
  3. myLayer.borderWidth = 3.0; 
注意:你可以将图层的背景设置成任何颜色,包括支持透明的颜色或使用模式图片。使用模式图片时,渲染是由Core Graphics完成,它使用的是标准坐标系统。标准坐标系统和iOS的坐标系统是不一样的。所以,图片在iOS上得渲染结果是颠倒的。可以使用[backgroundLayer setTransform:CATransform3DMakeScale(1.0, -1.0, 1.0)];解决该问题。
 
如果你将图层的背景颜色设置为不透明。考虑将图层的opaque属性设置为YES。当合成屏幕上的图层时可以提升系能,同时也会消除图层的后备存储管理alpha通道的要求。如果你给图层设置了非零的圆角半径,则不可以将图层标记为不透明类型。
 
图层支持圆角半径
你可以通过给图层添加一个圆角半径来创建一个圆角矩形。圆角半径是一个可视的装饰,它遮罩了图层边界矩形的部分区域以允许底层内容的显示。如图2-4所示,因为它包括了应用一个透明蒙版,圆角半径不影响图层的contents属性中得图片,除非masksToBounds属性被设置为YES。然而,圆角半径总是影响图层的背景颜色和边框的绘图方式。
图2-4 图层的圆角半径
 
为了设置圆角半径,只需要为图层的cornerRadius属性设置一个值。你指定的半径值是以点衡量的,并且圆角半径会应用到图层的四个角上。
 
图层内建支持阴影
CALayer类包括若干个配置阴影的属性。阴影让图层更加有深度,好像图层浮起来了。阴影也是另一种装饰。在具体的环境你会发现阴影非常的有用。使用图层你可以控制阴影的颜色,相对于图层内容的位置,透明度以及形状。
 
图层阴影的阴影透明度shadowOpacity默认被设置为0,这有效地隐藏了阴影。改变透明度为非0值将引起Core Animaiton绘制阴影。因为默认情况下,阴影被直接定位在图层的下方。所以你可能为了能够看到阴影而改变阴影的偏移量。你指定的偏移量被应用到图层上使用的是图层的本地坐标系。本地坐标系在iOS和OS X上的表现是不同的。如图2-5所示,图中显示了一个带有偏下偏右阴影的图层。在iOS中,这需要指定一个正值,而在OS X中需要指定一个负值。
图2-5 给图层设置阴影
 
给一个图层的contents属性赋值一张只有中间部分存在图像四周为透明的图片,并对图层施加阴影,阴影的偏移量为(5,5)。则该阴影只会出现在红色圆的偏右下出现。如果给图层的backgroundColor属性赋值,则此时的阴影就会出现在图层边界的偏右偏下方。图层产生阴影的条件包括设置图层的shadowOffset属性值、shadowOpacity属性值、图层或其子层的contents属性值或者backgroundColor属性值。
图2-6 四周透明的图片
 
当给图层添加阴影,阴影就是图层内容的一部分,但实际上阴影扩展到了图层边界矩形的外围。结果是,如果你启用了图层的masksToBounds属性,围绕边缘的阴影将被裁减掉。如果你的图层中含有任何透明的内容,这将引起一个古怪的效果,图层下方的阴影部分依然可以看见,但是超出图层边界的部分就没有了。如果既想要阴影又要启用maskToBounds为YES,那么你可以使用两个图层。第一个是包含内容的图层,将该图层的maskToBounds属性设置为YES,然后创建一个相同尺寸且含有阴影效果的第二个图层,最后调用第二个图层的addSublayer方法将第一个图层嵌入到第二个图层中就可以了。
 
注意:iOS平台上不能给图层添加滤镜
 
给图层添加自定义属性
CAAnimation和CALayer类扩展了键值编码以对自定义属性进行支持。你可以使用该行为给图层添加数据并使用你定义的键检索对应的值。你甚至可以给你的自定义属性关联动作,当该属性的值发生变化,对应的动画将会被执行。
 
打印图层支持的视图内容
在打印过程中,当需要适应打印环境时,图层将重绘它们的内容。尽管当向屏幕上做渲染的时Core Animation一般是依赖缓存位图。但在打印时它将重绘这些内容。特别情况下,如果支持图层的视图使用drawRect:方法提供图层内容,Core Animation将再次调用drawRect:方法以生成可打印的图层内容。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值